mirror of
https://github.com/esphome/esphome.git
synced 2026-10-26 16:02:13 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff25bc334a | ||
|
|
840f3216b8 | ||
|
|
e257b279d5 | ||
|
|
125bd8ccd0 | ||
|
|
cce3511112 | ||
|
|
836b3220a2 | ||
|
|
49f2610595 | ||
|
|
b49f3953f9 | ||
|
|
de9420a625 | ||
|
|
ae127f7db8 | ||
|
|
26ba2cc234 | ||
|
|
068fe3b7ce | ||
|
|
0af09caddc | ||
|
|
1154941f25 | ||
|
|
9a65dda424 | ||
|
|
81191f3d5c | ||
|
|
26c852f169 | ||
|
|
ebdf95be94 | ||
|
|
29259ca74e | ||
|
|
6a1b5d517b | ||
|
|
9687c0e0a8 | ||
|
|
230c120898 |
No files matched your search
+30
-1
@@ -1,15 +1,18 @@
|
||||
from collections.abc import Awaitable, Callable, Sequence
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
import math
|
||||
import string
|
||||
from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ABOVE,
|
||||
CONF_ALL,
|
||||
CONF_ANY,
|
||||
CONF_AUTOMATION_ID,
|
||||
CONF_BELOW,
|
||||
CONF_CONDITION,
|
||||
CONF_COUNT,
|
||||
CONF_ELSE,
|
||||
@@ -363,6 +366,25 @@ class ApplyField:
|
||||
return ApplyCall(target, ((self.conf_key, self.type_, self.const_fn),))
|
||||
|
||||
|
||||
def _in_range_low(config: ConfigType, value: float) -> str:
|
||||
return "-INFINITY" if math.isnan(value) else str(cg.safe_exp(value))
|
||||
|
||||
|
||||
def _in_range_high(config: ConfigType, value: float) -> str:
|
||||
# With no lower bound, an unset upper bound stays NaN so the check is false, as before.
|
||||
if math.isnan(value):
|
||||
return "NAN" if math.isnan(config[CONF_ABOVE]) else "INFINITY"
|
||||
return str(cg.safe_exp(value))
|
||||
|
||||
|
||||
# ``above <= state <= below`` on the parent's float ``state``; register with ``IN_RANGE_DEFAULTS``.
|
||||
IN_RANGE_CHECK = ApplyCall(
|
||||
"state >= {} && {parent}->state <= {}",
|
||||
((CONF_ABOVE, cg.float_, _in_range_low), (CONF_BELOW, cg.float_, _in_range_high)),
|
||||
)
|
||||
IN_RANGE_DEFAULTS = {CONF_ABOVE: math.nan, CONF_BELOW: math.nan}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ApplyBytes:
|
||||
"""A byte payload key forwarded as ``target``, whose ``{}`` stands for ``data, len``.
|
||||
@@ -691,7 +713,11 @@ async def _render_check(
|
||||
|
||||
|
||||
def register_apply_condition(
|
||||
name: str, schema: cv.Schema, check: str | ApplyCall, id_key: str = CONF_ID
|
||||
name: str,
|
||||
schema: cv.Schema,
|
||||
check: str | ApplyCall,
|
||||
id_key: str = CONF_ID,
|
||||
defaults: ConfigType | None = None,
|
||||
) -> None:
|
||||
"""Register a condition that is one expression on its parent, with no C++ class.
|
||||
|
||||
@@ -700,6 +726,7 @@ def register_apply_condition(
|
||||
against config values, all of which must be present. Write ``== false`` to negate.
|
||||
A ``cg.std_string`` constant stays in flash on ESP8266 and supports only ``==`` or ``!=``
|
||||
against a ``std::string`` or ``StringRef`` member; other string types are plain literals.
|
||||
``defaults`` fills absent keys at code generation only, so the validated config is unchanged.
|
||||
Generates one static predicate and an ``ApplyCondition`` templated on it.
|
||||
"""
|
||||
call = check if isinstance(check, ApplyCall) else ApplyCall(check)
|
||||
@@ -714,6 +741,8 @@ def register_apply_condition(
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
if defaults:
|
||||
config = {**defaults, **config}
|
||||
parent = await _apply_parent(config, id_key)
|
||||
lambda_args = _apply_lambda_args(args)
|
||||
values = _apply_values(config, members)
|
||||
|
||||
@@ -6,19 +6,11 @@ namespace esphome::as3935_i2c {
|
||||
ESPHOME_LOG_TAG(TAG, "as3935_i2c");
|
||||
|
||||
void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) {
|
||||
uint8_t write_reg;
|
||||
if (!this->read_byte(reg, &write_reg)) {
|
||||
this->mark_failed();
|
||||
ESP_LOGW(TAG, "read_byte failed - increase log level for more details!");
|
||||
return;
|
||||
}
|
||||
|
||||
write_reg &= (~mask);
|
||||
write_reg |= (bits << start_pos);
|
||||
|
||||
if (!this->write_byte(reg, write_reg)) {
|
||||
ESP_LOGW(TAG, "write_byte failed - increase log level for more details!");
|
||||
return;
|
||||
const uint8_t value = bits << start_pos;
|
||||
// Bits of value outside mask were ORed in before, so they stay part of the update
|
||||
// Also called at runtime, so a transient bus error only warns, like read_register()
|
||||
if (!this->update_register(reg, mask | value, value)) {
|
||||
ESP_LOGW(TAG, "Failed to update register 0x%02X", reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,17 +240,11 @@ bool AS7341Component::write_register_bit(uint8_t address, bool value, uint8_t bi
|
||||
}
|
||||
|
||||
bool AS7341Component::set_register_bit(uint8_t address, uint8_t bit_position) {
|
||||
uint8_t data;
|
||||
this->read_byte(address, &data);
|
||||
data |= (1 << bit_position);
|
||||
return this->write_byte(address, data);
|
||||
return this->update_register(address, 1 << bit_position, 1 << bit_position);
|
||||
}
|
||||
|
||||
bool AS7341Component::clear_register_bit(uint8_t address, uint8_t bit_position) {
|
||||
uint8_t data;
|
||||
this->read_byte(address, &data);
|
||||
data &= ~(1 << bit_position);
|
||||
return this->write_byte(address, data);
|
||||
return this->update_register(address, 1 << bit_position, 0);
|
||||
}
|
||||
|
||||
} // namespace esphome::as7341
|
||||
@@ -264,12 +264,7 @@ bool AS734XComponent::select_bank_(uint8_t reg) {
|
||||
}
|
||||
|
||||
bool AS734XComponent::update_bits_(uint8_t reg, uint8_t mask, bool value) {
|
||||
uint8_t data = 0;
|
||||
if (!this->select_bank_(reg) || !this->read_byte(reg, &data)) {
|
||||
return false;
|
||||
}
|
||||
data = value ? (data | mask) : (data & ~mask);
|
||||
return this->write_byte(reg, data);
|
||||
return this->select_bank_(reg) && this->update_register(reg, mask, value ? mask : 0);
|
||||
}
|
||||
|
||||
// The exposure is applied before every measurement, so a gain, ATIME or ASTEP changed at runtime
|
||||
|
||||
@@ -108,8 +108,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
full = fv.full_config.get()
|
||||
hub = full.get_config_for_path(full.get_path_for_id(config[CONF_AS734X_ID])[:-1])
|
||||
hub = fv.get_declaring_config(config[CONF_AS734X_ID])
|
||||
if hub[CONF_TYPE] != "AS7341":
|
||||
return config
|
||||
for key in _BAND_KEYS:
|
||||
|
||||
@@ -29,6 +29,7 @@ from esphome.components.libretiny.const import (
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.stacktrace import process_crash_handler_line as process_stacktrace # noqa: F401
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS
|
||||
|
||||
@@ -139,49 +139,24 @@ bool BME680Component::setup_registers_() {
|
||||
this->calibration_.ambient_temperature = 25; // prime ambient temperature
|
||||
|
||||
// Config register
|
||||
uint8_t config_register;
|
||||
if (!this->read_byte(BME680_REGISTER_CONFIG, &config_register)) {
|
||||
return false;
|
||||
}
|
||||
config_register &= ~0b00011100;
|
||||
config_register |= (this->iir_filter_ & 0b111) << 2;
|
||||
if (!this->write_byte(BME680_REGISTER_CONFIG, config_register)) {
|
||||
if (!this->update_register(BME680_REGISTER_CONFIG, 0b00011100, (this->iir_filter_ & 0b111) << 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Humidity control register
|
||||
uint8_t hum_control;
|
||||
if (!this->read_byte(BME680_REGISTER_CONTROL_HUMIDITY, &hum_control)) {
|
||||
return false;
|
||||
}
|
||||
hum_control &= ~0b00000111;
|
||||
hum_control |= this->humidity_oversampling_ & 0b111;
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_HUMIDITY, hum_control)) {
|
||||
if (!this->update_register(BME680_REGISTER_CONTROL_HUMIDITY, 0b00000111, this->humidity_oversampling_ & 0b111)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gas 1 control register
|
||||
uint8_t gas1_control;
|
||||
if (!this->read_byte(BME680_REGISTER_CONTROL_GAS1, &gas1_control)) {
|
||||
return false;
|
||||
}
|
||||
gas1_control &= ~0b00011111;
|
||||
gas1_control |= 1 << 4;
|
||||
gas1_control |= 0; // profile 0
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_GAS1, gas1_control)) {
|
||||
// Gas 1 control register: run gas, heater profile 0
|
||||
if (!this->update_register(BME680_REGISTER_CONTROL_GAS1, 0b00011111, 1 << 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool heat_off = this->heater_temperature_ == 0 || this->heater_duration_ == 0;
|
||||
|
||||
// Gas 0 control register
|
||||
uint8_t gas0_control;
|
||||
if (!this->read_byte(BME680_REGISTER_CONTROL_GAS0, &gas0_control)) {
|
||||
return false;
|
||||
}
|
||||
gas0_control &= ~0b00001000;
|
||||
gas0_control |= heat_off << 3;
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_GAS0, gas0_control)) {
|
||||
if (!this->update_register(BME680_REGISTER_CONTROL_GAS0, 0b00001000, heat_off << 3)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,7 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
if config[CONF_TYPE] not in _MAGNETIC_FIELDS:
|
||||
return
|
||||
full_config = fv.full_config.get()
|
||||
bmi270_path = full_config.get_path_for_id(config[CONF_BMI270_ID])[:-1]
|
||||
bmi270_config = full_config.get_config_for_path(bmi270_path)
|
||||
bmi270_config = fv.get_declaring_config(config[CONF_BMI270_ID])
|
||||
if bmi270_config.get(CONF_AUX_DEVICE) != "BMM150":
|
||||
raise cv.Invalid(
|
||||
"This sensor requires the parent bmi270's 'aux_device' to be set to 'BMM150'",
|
||||
|
||||
@@ -7,6 +7,7 @@ from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_UPDATE_INTERVAL, CONF_WATCHDOG
|
||||
from esphome.core import CORE, ID, EsphomeError, Lambda, TimePeriod
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType, TemplateArgsType
|
||||
|
||||
CODEOWNERS = ["@Rapsssito"]
|
||||
@@ -359,8 +360,7 @@ async def bq25186_configure_to_code(
|
||||
parent = await cg.get_variable(config[CONF_ID])
|
||||
|
||||
# Options the action leaves out fall back to the component's configuration
|
||||
path = CORE.config.get_path_for_id(config[CONF_ID])[:-1]
|
||||
parent_config = CORE.config.get_config_for_path(path)
|
||||
parent_config = fv.get_declaring_config(config[CONF_ID], CORE.config)
|
||||
merged = {**parent_config, **config}
|
||||
try:
|
||||
_check_watchdog(merged)
|
||||
|
||||
@@ -28,13 +28,10 @@ bool BQ25186Component::write_register_(uint8_t reg, uint8_t value) {
|
||||
}
|
||||
|
||||
bool BQ25186Component::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
|
||||
uint8_t reg_value;
|
||||
if (!this->read_byte(reg, ®_value)) {
|
||||
ESP_LOGW(TAG, "Failed to read register 0x%02X", reg);
|
||||
return false;
|
||||
}
|
||||
reg_value = (reg_value & ~mask) | (value & mask);
|
||||
return this->write_register_(reg, reg_value);
|
||||
if (this->update_register(reg, mask, value))
|
||||
return true;
|
||||
ESP_LOGW(TAG, "Failed to update register 0x%02X", reg);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t BQ25186Component::expected_register_(size_t index) const {
|
||||
|
||||
@@ -71,12 +71,12 @@ class BQ25186Component : public PollingComponent, public i2c::I2CDevice {
|
||||
|
||||
protected:
|
||||
bool write_register_(uint8_t reg, uint8_t value);
|
||||
bool update_register_(uint8_t reg, uint8_t mask, uint8_t value);
|
||||
uint8_t expected_register_(size_t index) const;
|
||||
bool config_matches_(const BQ25186StatusRegisters ®s) const;
|
||||
#ifdef USE_BQ25186_PG_GPO
|
||||
void check_pg_gpo_(const BQ25186StatusRegisters &status, bool &pending);
|
||||
#endif
|
||||
bool update_register_(uint8_t reg, uint8_t mask, uint8_t value);
|
||||
|
||||
#ifdef BQ25186_BINARY_SENSOR_COUNT
|
||||
StaticVector<BQ25186BinarySensor *, BQ25186_BINARY_SENSOR_COUNT> binary_sensors_;
|
||||
|
||||
@@ -42,10 +42,8 @@ void CAP1188Component::finish_setup_() {
|
||||
}
|
||||
|
||||
// Set sensitivity
|
||||
uint8_t sensitivity = 0;
|
||||
this->read_byte(CAP1188_SENSITVITY, &sensitivity);
|
||||
sensitivity = sensitivity & 0x0f;
|
||||
this->write_byte(CAP1188_SENSITVITY, sensitivity | this->touch_threshold_);
|
||||
// The threshold replaces the upper nibble and is ORed into the lower one, as before
|
||||
this->update_register(CAP1188_SENSITVITY, 0xF0 | this->touch_threshold_, this->touch_threshold_);
|
||||
|
||||
// Allow multiple touches
|
||||
this->write_byte(CAP1188_MULTI_TOUCH, this->allow_multiple_touches_);
|
||||
|
||||
@@ -3,8 +3,7 @@ from collections.abc import Callable
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MODE, CONF_MODEL
|
||||
from esphome.core import ID
|
||||
from esphome.const import CONF_MODE, CONF_MODEL
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -59,16 +58,11 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
)
|
||||
|
||||
|
||||
def get_hub_config(root: ConfigType, hub_id: ID) -> ConfigType:
|
||||
"""Return the configuration of the hub with the given id."""
|
||||
return next(conf for conf in root[DOMAIN] if conf[CONF_ID] == hub_id)
|
||||
|
||||
|
||||
def final_validate_mode(keys: dict[str, str]) -> Callable[[ConfigType], None]:
|
||||
"""Reject entity keys that need a different hub mode; `keys` maps each key to the mode it needs."""
|
||||
|
||||
def validator(config: ConfigType) -> None:
|
||||
hub = get_hub_config(fv.full_config.get(), config[CONF_DFROBOT_SEN0609_ID])
|
||||
hub = fv.get_declaring_config(config[CONF_DFROBOT_SEN0609_ID])
|
||||
for key, mode in keys.items():
|
||||
if key in config and hub[CONF_MODE] != mode:
|
||||
raise cv.Invalid(
|
||||
|
||||
@@ -2,17 +2,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
from esphome.components.const import CONF_QUERY_PARAMS
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_ID,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_DATABASE,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
from esphome.const import CONF_FACTORY_RESET, CONF_ID, CONF_RESTART
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_DFROBOT_SEN0609_ID, DFRobotSEN0609Component, dfrobot_sen0609_ns
|
||||
@@ -23,23 +13,11 @@ ButtonAction = dfrobot_sen0609_ns.enum("ButtonAction")
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_DFROBOT_SEN0609_ID): cv.use_id(DFRobotSEN0609Component),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
DFRobotSEN0609Button,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_QUERY_PARAMS): button.button_schema(
|
||||
DFRobotSEN0609Button,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_DATABASE,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
DFRobotSEN0609Button,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
cv.Optional(CONF_FACTORY_RESET): button.factory_reset_button_schema(
|
||||
DFRobotSEN0609Button
|
||||
),
|
||||
cv.Optional(CONF_QUERY_PARAMS): button.query_button_schema(DFRobotSEN0609Button),
|
||||
cv.Optional(CONF_RESTART): button.restart_button_schema(DFRobotSEN0609Button),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -132,9 +132,9 @@ void DFRobotSEN0609Component::dump_config() {
|
||||
LOG_BINARY_SENSOR(" ", "HasTarget", this->has_target_binary_sensor_);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "TargetDistance", this->target_distance_sensor_);
|
||||
LOG_SENSOR(" ", "TargetSpeed", this->target_speed_sensor_);
|
||||
LOG_SENSOR(" ", "TargetEnergy", this->target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetDistance", this->target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetSpeed", this->target_speed_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetEnergy", this->target_energy_sensor_);
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
LOG_TEXT_SENSOR(" ", "Version", this->version_text_sensor_);
|
||||
@@ -324,17 +324,18 @@ void DFRobotSEN0609Component::log_bad_frame_(const LogString *problem, const cha
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
static void publish_deduplicated(sensor::Sensor *sens, Deduplicator<float> &dedup, float value) {
|
||||
if (sens == nullptr)
|
||||
return;
|
||||
if (std::isnan(value) ? dedup.next_unknown() : dedup.next(value))
|
||||
sens->publish_state(value);
|
||||
static void publish_deduplicated(sensor::SensorWithDedup<float> &sens, float value) {
|
||||
if (std::isnan(value)) {
|
||||
sens.publish_state_unknown();
|
||||
} else {
|
||||
sens.publish_state_if_not_dup(value);
|
||||
}
|
||||
}
|
||||
|
||||
void DFRobotSEN0609Component::publish_target_(float distance, float speed, float energy) {
|
||||
publish_deduplicated(this->target_distance_sensor_, this->distance_dedup_, distance);
|
||||
publish_deduplicated(this->target_speed_sensor_, this->speed_dedup_, speed);
|
||||
publish_deduplicated(this->target_energy_sensor_, this->energy_dedup_, energy);
|
||||
publish_deduplicated(this->target_distance_sensor_, distance);
|
||||
publish_deduplicated(this->target_speed_sensor_, speed);
|
||||
publish_deduplicated(this->target_energy_sensor_, energy);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/sensor/sensor_with_dedup.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
@@ -103,9 +103,9 @@ class DFRobotSEN0609Component final : public Component, public uart::UARTDevice
|
||||
SUB_BINARY_SENSOR(has_target)
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
SUB_SENSOR(target_distance)
|
||||
SUB_SENSOR(target_speed)
|
||||
SUB_SENSOR(target_energy)
|
||||
SUB_SENSOR_WITH_DEDUP(target_distance, float)
|
||||
SUB_SENSOR_WITH_DEDUP(target_speed, float)
|
||||
SUB_SENSOR_WITH_DEDUP(target_energy, float)
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
SUB_TEXT_SENSOR(version)
|
||||
@@ -175,9 +175,6 @@ class DFRobotSEN0609Component final : public Component, public uart::UARTDevice
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
void publish_target_(float distance, float speed, float energy);
|
||||
Deduplicator<float> distance_dedup_;
|
||||
Deduplicator<float> speed_dedup_;
|
||||
Deduplicator<float> energy_dedup_;
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
|
||||
@@ -18,6 +18,7 @@ from esphome.const import (
|
||||
UNIT_SECOND,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import (
|
||||
@@ -28,7 +29,6 @@ from .. import (
|
||||
DFRobotSEN0609Component,
|
||||
dfrobot_sen0609_ns,
|
||||
final_validate_mode,
|
||||
get_hub_config,
|
||||
)
|
||||
|
||||
DFRobotSEN0609Number = dfrobot_sen0609_ns.class_("DFRobotSEN0609Number", number.Number)
|
||||
@@ -181,7 +181,7 @@ FINAL_VALIDATE_SCHEMA = final_validate_mode(
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub_id = config[CONF_DFROBOT_SEN0609_ID]
|
||||
hub = await cg.get_variable(hub_id)
|
||||
max_range = MAX_RANGES[get_hub_config(CORE.config, hub_id)[CONF_MODEL]]
|
||||
max_range = MAX_RANGES[fv.get_declaring_config(hub_id, CORE.config)[CONF_MODEL]]
|
||||
numbers = number.sub_numbers(config, parent=hub)
|
||||
for s in SETTINGS:
|
||||
await numbers(
|
||||
|
||||
@@ -28,7 +28,7 @@ from esphome.const import (
|
||||
SCHEDULER_DONT_RUN,
|
||||
)
|
||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||
from esphome.final_validate import full_config
|
||||
from esphome.final_validate import full_config, get_declaring_config
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DOMAIN = "display"
|
||||
@@ -225,9 +225,7 @@ def get_display_metadata(display_id: ID) -> DisplayMetaData:
|
||||
return meta
|
||||
# No metadata found, display driver may not yet support it.
|
||||
# Read the raw config to populate the returned data
|
||||
global_config = full_config.get()
|
||||
path = global_config.get_path_for_id(display_id)[:-1]
|
||||
disp_config = global_config.get_config_for_path(path)
|
||||
disp_config = get_declaring_config(display_id)
|
||||
dimensions = disp_config.get(CONF_DIMENSIONS, (0, 0))
|
||||
if isinstance(dimensions, dict):
|
||||
dimensions = (dimensions.get(CONF_WIDTH, 0), dimensions.get(CONF_HEIGHT, 0))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ds248x_one_wire_bus.h"
|
||||
#include "ds248x.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ds248x {
|
||||
@@ -151,11 +152,7 @@ uint64_t DS248xOneWireBus::search_int() {
|
||||
}
|
||||
|
||||
// Update address based on direction taken
|
||||
if (dir_taken) {
|
||||
address |= bit_mask;
|
||||
} else {
|
||||
address &= ~bit_mask;
|
||||
}
|
||||
assign_bits(address, bit_mask, dir_taken);
|
||||
}
|
||||
|
||||
// Search successful
|
||||
|
||||
@@ -32,9 +32,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
"""Validate that the channel is within the parent's channel count."""
|
||||
fconf = fv.full_config.get()
|
||||
path = fconf.get_path_for_id(config[CONF_DS248X_ID])[:-1]
|
||||
parent_config = fconf.get_config_for_path(path)
|
||||
parent_config = fv.get_declaring_config(config[CONF_DS248X_ID])
|
||||
channel_count = get_channel_count(parent_config)
|
||||
channel = config[CONF_CHANNEL]
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ void ES7210::setup() {
|
||||
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));
|
||||
ES7210_ERROR_FAILED(this->update_register(ES7210_MODE_CONFIG_REG08, 0x01, 0x00));
|
||||
|
||||
// Configure analog power
|
||||
ES7210_ERROR_FAILED(this->write_byte(ES7210_ANALOG_REG40, 0xC3));
|
||||
@@ -141,39 +141,39 @@ bool ES7210::configure_sample_rate_() {
|
||||
|
||||
bool ES7210::configure_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->update_register(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->update_register(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,
|
||||
this->es7210_gain_reg_value_(this->channel_gains_[0])));
|
||||
ES7210_ERROR_CHECK(this->update_register(ES7210_MIC1_GAIN_REG43, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(
|
||||
this->update_register(ES7210_MIC1_GAIN_REG43, 0x0f, this->es7210_gain_reg_value_(this->channel_gains_[0])));
|
||||
|
||||
// Configure mic 2
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
|
||||
ES7210_ERROR_CHECK(this->update_register(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,
|
||||
this->es7210_gain_reg_value_(this->channel_gains_[1])));
|
||||
ES7210_ERROR_CHECK(this->update_register(ES7210_MIC2_GAIN_REG44, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(
|
||||
this->update_register(ES7210_MIC2_GAIN_REG44, 0x0f, this->es7210_gain_reg_value_(this->channel_gains_[1])));
|
||||
|
||||
// Configure mic 3
|
||||
// MIC3 uses the ADC3/4 and MIC3/4 clock domains (bits 2 and 4), not the MIC1/2 domains.
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x15, 0x00));
|
||||
ES7210_ERROR_CHECK(this->update_register(ES7210_CLOCK_OFF_REG01, 0x15, 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,
|
||||
this->es7210_gain_reg_value_(this->channel_gains_[2])));
|
||||
ES7210_ERROR_CHECK(this->update_register(ES7210_MIC3_GAIN_REG45, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(
|
||||
this->update_register(ES7210_MIC3_GAIN_REG45, 0x0f, this->es7210_gain_reg_value_(this->channel_gains_[2])));
|
||||
|
||||
// Configure mic 4
|
||||
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x15, 0x00));
|
||||
ES7210_ERROR_CHECK(this->update_register(ES7210_CLOCK_OFF_REG01, 0x15, 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,
|
||||
this->es7210_gain_reg_value_(this->channel_gains_[3])));
|
||||
ES7210_ERROR_CHECK(this->update_register(ES7210_MIC4_GAIN_REG46, 0x10, 0x10));
|
||||
ES7210_ERROR_CHECK(
|
||||
this->update_register(ES7210_MIC4_GAIN_REG46, 0x0f, this->es7210_gain_reg_value_(this->channel_gains_[3])));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -227,11 +227,4 @@ bool ES7210::configure_i2s_format_() {
|
||||
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 esphome::es7210
|
||||
@@ -40,13 +40,6 @@ class ES7210 final : public audio_adc::AudioAdc, public Component, public i2c::I
|
||||
float mic_gain() override { return this->channel_gains_[0]; };
|
||||
|
||||
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
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
from esphome.components.audio_dac import AudioDac
|
||||
from esphome.components.speaker import DOMAIN as SPEAKER_DOMAIN
|
||||
from esphome.components.speaker import DOMAIN as SPEAKER_DOMAIN, get_audio_dac_ids
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_AUDIO_DAC, CONF_BITS_PER_SAMPLE, CONF_ID
|
||||
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -30,10 +30,8 @@ def _final_validate(config: ConfigType) -> None:
|
||||
# Check all speaker configurations for ones that reference this es8156
|
||||
speaker_configs = full_config.get(SPEAKER_DOMAIN, [])
|
||||
for speaker_config in speaker_configs:
|
||||
audio_dac_id = speaker_config.get(CONF_AUDIO_DAC)
|
||||
if (
|
||||
audio_dac_id is not None
|
||||
and audio_dac_id == config[CONF_ID]
|
||||
config[CONF_ID] in get_audio_dac_ids(speaker_config)
|
||||
and (bits_per_sample := speaker_config.get(CONF_BITS_PER_SAMPLE))
|
||||
is not None
|
||||
and bits_per_sample > 24
|
||||
|
||||
@@ -91,20 +91,9 @@ float ES8156::volume() {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
assign_bits(reg13, BIT(1) | BIT(2), mute_state);
|
||||
|
||||
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);
|
||||
const uint8_t mask = BIT(1) | BIT(2);
|
||||
return this->update_register(ES8156_REG13_DAC_MUTE, mask, mute_state ? mask : 0);
|
||||
}
|
||||
|
||||
} // namespace esphome::es8156
|
||||
@@ -138,12 +138,9 @@ bool ES8311::configure_clock_() {
|
||||
}
|
||||
|
||||
// Register 0x02
|
||||
uint8_t reg02;
|
||||
ES8311_ERROR_CHECK(this->read_byte(ES8311_REG02_CLK_MANAGER, ®02));
|
||||
reg02 &= 0x07;
|
||||
reg02 |= (coefficient->pre_div - 1) << 5;
|
||||
reg02 |= encode_pre_mult(coefficient->pre_mult) << 3;
|
||||
ES8311_ERROR_CHECK(this->write_byte(ES8311_REG02_CLK_MANAGER, reg02));
|
||||
ES8311_ERROR_CHECK(
|
||||
this->update_register(ES8311_REG02_CLK_MANAGER, 0xF8,
|
||||
((coefficient->pre_div - 1) << 5) | (encode_pre_mult(coefficient->pre_mult) << 3)));
|
||||
|
||||
// Register 0x03
|
||||
const uint8_t reg03 = (coefficient->fs_mode << 6) | coefficient->adc_osr;
|
||||
@@ -157,23 +154,13 @@ bool ES8311::configure_clock_() {
|
||||
ES8311_ERROR_CHECK(this->write_byte(ES8311_REG05_CLK_MANAGER, reg05));
|
||||
|
||||
// Register 0x06
|
||||
uint8_t reg06;
|
||||
ES8311_ERROR_CHECK(this->read_byte(ES8311_REG06_CLK_MANAGER, ®06));
|
||||
assign_bits(reg06, BIT(5), this->sclk_inverted_);
|
||||
reg06 &= 0xE0;
|
||||
if (coefficient->bclk_div < 19) {
|
||||
reg06 |= (coefficient->bclk_div - 1) << 0;
|
||||
} else {
|
||||
reg06 |= (coefficient->bclk_div) << 0;
|
||||
}
|
||||
ES8311_ERROR_CHECK(this->write_byte(ES8311_REG06_CLK_MANAGER, reg06));
|
||||
uint8_t reg06 = coefficient->bclk_div < 19 ? coefficient->bclk_div - 1 : coefficient->bclk_div;
|
||||
if (this->sclk_inverted_)
|
||||
reg06 |= BIT(5);
|
||||
ES8311_ERROR_CHECK(this->update_register(ES8311_REG06_CLK_MANAGER, BIT(5) | 0x1F, reg06));
|
||||
|
||||
// Register 0x07
|
||||
uint8_t reg07;
|
||||
ES8311_ERROR_CHECK(this->read_byte(ES8311_REG07_CLK_MANAGER, ®07));
|
||||
reg07 &= 0xC0;
|
||||
reg07 |= coefficient->lrck_h << 0;
|
||||
ES8311_ERROR_CHECK(this->write_byte(ES8311_REG07_CLK_MANAGER, reg07));
|
||||
ES8311_ERROR_CHECK(this->update_register(ES8311_REG07_CLK_MANAGER, 0x3F, coefficient->lrck_h));
|
||||
|
||||
// Register 0x08
|
||||
ES8311_ERROR_CHECK(this->write_byte(ES8311_REG08_CLK_MANAGER, coefficient->lrck_l));
|
||||
@@ -184,10 +171,7 @@ bool ES8311::configure_clock_() {
|
||||
|
||||
bool ES8311::configure_format_() {
|
||||
// Configure I2S mode and format
|
||||
uint8_t reg00;
|
||||
ES8311_ERROR_CHECK(this->read_byte(ES8311_REG00_RESET, ®00));
|
||||
reg00 &= 0xBF;
|
||||
ES8311_ERROR_CHECK(this->write_byte(ES8311_REG00_RESET, reg00));
|
||||
ES8311_ERROR_CHECK(this->update_register(ES8311_REG00_RESET, BIT(6), 0));
|
||||
|
||||
// Configure SDP in resolution
|
||||
uint8_t reg09 = calculate_resolution_value(this->resolution_in_);
|
||||
@@ -216,17 +200,9 @@ bool ES8311::configure_mic_() {
|
||||
}
|
||||
|
||||
bool ES8311::set_mute_state_(bool mute_state) {
|
||||
uint8_t reg31;
|
||||
|
||||
this->is_muted_ = mute_state;
|
||||
|
||||
if (!this->read_byte(ES8311_REG31_DAC, ®31)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
assign_bits(reg31, BIT(6) | BIT(5), mute_state);
|
||||
|
||||
return this->write_byte(ES8311_REG31_DAC, reg31);
|
||||
const uint8_t mask = BIT(6) | BIT(5);
|
||||
return this->update_register(ES8311_REG31_DAC, mask, mute_state ? mask : 0);
|
||||
}
|
||||
|
||||
} // namespace esphome::es8311
|
||||
@@ -17,16 +17,20 @@ void IPAddressEthernetInfo::on_ip_state(const network::IPAddresses &ips, const n
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
ips[0].str_to(buf);
|
||||
this->publish_state(buf);
|
||||
#ifdef ETHERNET_INFO_IP_SENSOR_COUNT
|
||||
uint8_t sensor = 0;
|
||||
for (const auto &ip : ips) {
|
||||
if (ip.is_set()) {
|
||||
if (this->ip_sensors_[sensor] != nullptr) {
|
||||
ip.str_to(buf);
|
||||
this->ip_sensors_[sensor]->publish_state(buf);
|
||||
}
|
||||
sensor++;
|
||||
if (!ip.is_set())
|
||||
continue;
|
||||
if (sensor == ETHERNET_INFO_IP_SENSOR_COUNT)
|
||||
break;
|
||||
if (this->ip_sensors_[sensor] != nullptr) {
|
||||
ip.str_to(buf);
|
||||
this->ip_sensors_[sensor]->publish_state(buf);
|
||||
}
|
||||
sensor++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DNSAddressEthernetInfo::setup() { ethernet::global_eth_component->add_ip_state_listener(this); }
|
||||
|
||||
@@ -18,14 +18,19 @@ class IPAddressEthernetInfo final : public Component,
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
#ifdef ETHERNET_INFO_IP_SENSOR_COUNT
|
||||
void add_ip_sensors(uint8_t index, text_sensor::TextSensor *s) { this->ip_sensors_[index] = s; }
|
||||
#endif
|
||||
|
||||
// EthernetIPStateListener interface
|
||||
void on_ip_state(const network::IPAddresses &ips, const network::IPAddress &dns1,
|
||||
const network::IPAddress &dns2) override;
|
||||
|
||||
protected:
|
||||
std::array<text_sensor::TextSensor *, 5> ip_sensors_{};
|
||||
#ifdef ETHERNET_INFO_IP_SENSOR_COUNT
|
||||
// Sized to the highest configured address_N
|
||||
std::array<text_sensor::TextSensor *, ETHERNET_INFO_IP_SENSOR_COUNT> ip_sensors_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
class DNSAddressEthernetInfo final : public Component,
|
||||
|
||||
@@ -12,6 +12,7 @@ from esphome.types import ConfigType
|
||||
DEPENDENCIES = ["ethernet"]
|
||||
|
||||
ethernet_info_ns = cg.esphome_ns.namespace("ethernet_info")
|
||||
_request_ip_sensor_slot = cg.slot_counter("ETHERNET_INFO_IP_SENSOR_COUNT")
|
||||
|
||||
IPAddressEthernetInfo = ethernet_info_ns.class_(
|
||||
"IPAddressEthernetInfo", text_sensor.TextSensor, cg.Component
|
||||
@@ -57,10 +58,13 @@ async def to_code(config: ConfigType) -> None:
|
||||
if conf := config.get(CONF_IP_ADDRESS):
|
||||
ip_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS])
|
||||
await cg.register_component(ip_info, config[CONF_IP_ADDRESS])
|
||||
for x in range(5):
|
||||
if sensor_conf := conf.get(f"address_{x}"):
|
||||
sens = await text_sensor.new_text_sensor(sensor_conf)
|
||||
cg.add(ip_info.add_ip_sensors(x, sens))
|
||||
indexes = [x for x in range(5) if f"address_{x}" in conf]
|
||||
# One shared array size: the largest highest index of any entry, so add_ip_sensors(x) stays in range
|
||||
for _ in range(max(indexes, default=-1) + 1):
|
||||
_request_ip_sensor_slot(str(ip_info))
|
||||
for x in indexes:
|
||||
sens = await text_sensor.new_text_sensor(conf[f"address_{x}"])
|
||||
cg.add(ip_info.add_ip_sensors(x, sens))
|
||||
if conf := config.get(CONF_DNS_ADDRESS):
|
||||
dns_info = await text_sensor.new_text_sensor(conf)
|
||||
await cg.register_component(dns_info, conf)
|
||||
|
||||
@@ -23,10 +23,7 @@ CONF_REQUEST_VOLTAGE = "request_voltage"
|
||||
fusb302b_ns = cg.esphome_ns.namespace("fusb302b")
|
||||
FUSB302B = fusb302b_ns.class_("FUSB302B", cg.Component, i2c.I2CDevice)
|
||||
|
||||
ConnectForwarder = fusb302b_ns.class_("ConnectForwarder")
|
||||
DisconnectForwarder = fusb302b_ns.class_("DisconnectForwarder")
|
||||
PowerReadyForwarder = fusb302b_ns.class_("PowerReadyForwarder")
|
||||
ErrorForwarder = fusb302b_ns.class_("ErrorForwarder")
|
||||
PdState = fusb302b_ns.enum("PdState", is_class=True)
|
||||
|
||||
# USB PD fixed supply voltages are 5, 9, 12, 15 and 20 V; the highest one that does not exceed the
|
||||
# requested voltage is chosen.
|
||||
@@ -50,18 +47,43 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.only_on_esp32,
|
||||
)
|
||||
|
||||
_STATE_PARAMS = [(PdState, "state"), (PdState, "previous")]
|
||||
_CONNECTED = "fusb302b::is_connected_state"
|
||||
|
||||
# Each state trigger fires on the change described by its filter.
|
||||
_STATE_TRIGGERS = (
|
||||
# A source is attached, including after recovering from a communication error.
|
||||
(CONF_ON_CONNECT, f"{_CONNECTED}(state) && !{_CONNECTED}(previous)"),
|
||||
# The source is removed, or is no longer known to be attached because communication failed.
|
||||
(CONF_ON_DISCONNECT, f"!{_CONNECTED}(state) && {_CONNECTED}(previous)"),
|
||||
# Negotiation is finished: a new PD contract is in place, or the source does not speak PD.
|
||||
(
|
||||
CONF_ON_POWER_READY,
|
||||
(
|
||||
"state == fusb302b::PdState::PD_STATE_EXPLICIT_CONTRACT"
|
||||
" || state == fusb302b::PdState::PD_STATE_PD_TIMEOUT"
|
||||
),
|
||||
),
|
||||
# Communication with the chip fails.
|
||||
(
|
||||
CONF_ON_ERROR,
|
||||
(
|
||||
"state == fusb302b::PdState::PD_STATE_ERROR"
|
||||
" && previous != fusb302b::PdState::PD_STATE_ERROR"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
_CALLBACK_AUTOMATIONS = (
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_CONNECT, "add_on_state_callback", forwarder=ConnectForwarder
|
||||
),
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_DISCONNECT, "add_on_state_callback", forwarder=DisconnectForwarder
|
||||
),
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_POWER_READY, "add_on_state_callback", forwarder=PowerReadyForwarder
|
||||
),
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_ERROR, "add_on_state_callback", forwarder=ErrorForwarder
|
||||
*(
|
||||
automation.CallbackAutomation(
|
||||
key,
|
||||
"add_on_state_callback",
|
||||
params=_STATE_PARAMS,
|
||||
forward=[],
|
||||
when=when,
|
||||
)
|
||||
for key, when in _STATE_TRIGGERS
|
||||
),
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_REQUEST_FAILED, "add_on_request_failed_callback"
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "pd.h"
|
||||
|
||||
namespace esphome::fusb302b {
|
||||
|
||||
/// Triggers when a source is attached, including after recovering from a communication error.
|
||||
struct ConnectForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(PdState state, PdState previous) const {
|
||||
if (is_connected_state(state) && !is_connected_state(previous))
|
||||
this->automation->trigger();
|
||||
}
|
||||
};
|
||||
|
||||
/// Triggers when the source is removed, or is no longer known to be attached because communication failed.
|
||||
struct DisconnectForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(PdState state, PdState previous) const {
|
||||
if (!is_connected_state(state) && is_connected_state(previous))
|
||||
this->automation->trigger();
|
||||
}
|
||||
};
|
||||
|
||||
/// Triggers when negotiation is finished: a new PD contract is in place, or the source does not speak PD.
|
||||
struct PowerReadyForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(PdState state, PdState /*previous*/) const {
|
||||
if (state == PdState::PD_STATE_EXPLICIT_CONTRACT || state == PdState::PD_STATE_PD_TIMEOUT)
|
||||
this->automation->trigger();
|
||||
}
|
||||
};
|
||||
|
||||
/// Triggers when communication with the chip fails.
|
||||
struct ErrorForwarder {
|
||||
Automation<> *automation;
|
||||
void operator()(PdState state, PdState previous) const {
|
||||
if (state == PdState::PD_STATE_ERROR && previous != PdState::PD_STATE_ERROR)
|
||||
this->automation->trigger();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(ConnectForwarder) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<ConnectForwarder>);
|
||||
static_assert(sizeof(DisconnectForwarder) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<DisconnectForwarder>);
|
||||
static_assert(sizeof(PowerReadyForwarder) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<PowerReadyForwarder>);
|
||||
static_assert(sizeof(ErrorForwarder) <= sizeof(void *));
|
||||
static_assert(std::is_trivially_copyable_v<ErrorForwarder>);
|
||||
|
||||
} // namespace esphome::fusb302b
|
||||
@@ -265,12 +265,8 @@ bool FUSB302B::init_chip_() {
|
||||
return false;
|
||||
|
||||
// Let the chip retransmit unacknowledged messages up to 3 times
|
||||
uint8_t control3 = 0;
|
||||
if (!this->read_byte(FUSB_CONTROL3, &control3))
|
||||
return false;
|
||||
control3 =
|
||||
(control3 & ~FUSB_CONTROL3_N_RETRIES_MASK) | (3 << FUSB_CONTROL3_N_RETRIES_SHIFT) | FUSB_CONTROL3_AUTO_RETRY;
|
||||
if (!this->write_byte(FUSB_CONTROL3, control3))
|
||||
if (!this->update_register(FUSB_CONTROL3, FUSB_CONTROL3_N_RETRIES_MASK | FUSB_CONTROL3_AUTO_RETRY,
|
||||
(3 << FUSB_CONTROL3_N_RETRIES_SHIFT) | FUSB_CONTROL3_AUTO_RETRY))
|
||||
return false;
|
||||
|
||||
if (!this->write_byte(FUSB_POWER, FUSB_POWER_ALL))
|
||||
|
||||
@@ -63,9 +63,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def _validate_model(config: ConfigType) -> None:
|
||||
full_config = fv.full_config.get()
|
||||
climate_path = full_config.get_path_for_id(config[CONF_GREE_ID])[:-1]
|
||||
climate_conf = full_config.get_config_for_path(climate_path)
|
||||
climate_conf = fv.get_declaring_config(config[CONF_GREE_ID])
|
||||
if climate_conf[CONF_MODEL] not in SUPPORTED_MODELS:
|
||||
raise cv.Invalid(
|
||||
"Gree switches are only supported for the "
|
||||
|
||||
@@ -52,12 +52,10 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def _final_validate(config) -> None:
|
||||
full_config = fv.full_config.get()
|
||||
for switch_type in [CONF_BEEPER, CONF_QUIET_MODE]:
|
||||
# Check switches that are only supported for HonClimate
|
||||
if config.get(switch_type):
|
||||
climate_path = full_config.get_path_for_id(config[CONF_HAIER_ID])[:-1]
|
||||
climate_conf = full_config.get_config_for_path(climate_path)
|
||||
climate_conf = fv.get_declaring_config(config[CONF_HAIER_ID])
|
||||
protocol_type = climate_conf.get(CONF_PROTOCOL)
|
||||
if protocol_type.casefold() != PROTOCOL_HON.casefold():
|
||||
raise cv.Invalid(
|
||||
|
||||
@@ -3,9 +3,8 @@ import esphome.codegen as cg
|
||||
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||
from esphome.core import ID, coroutine_with_priority
|
||||
from esphome.core import coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_HTTP_REQUEST_ID, HttpRequestComponent, http_request_ns
|
||||
@@ -21,9 +20,6 @@ CONF_MD5_URL = "md5_url"
|
||||
OtaHttpRequestComponent = http_request_ns.class_(
|
||||
"OtaHttpRequestComponent", OTAComponent
|
||||
)
|
||||
OtaHttpRequestComponentFlashAction = http_request_ns.class_(
|
||||
"OtaHttpRequestComponentFlashAction", automation.Action
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
@@ -68,38 +64,13 @@ OTA_HTTP_REQUEST_FLASH_ACTION_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"ota.http_request.flash",
|
||||
OtaHttpRequestComponentFlashAction,
|
||||
OTA_HTTP_REQUEST_FLASH_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_MD5_URL, "set_md5_url", cg.std_string),
|
||||
automation.ApplyField(CONF_MD5, "set_md5", cg.std_string),
|
||||
automation.ApplyField(CONF_PASSWORD, "set_password", cg.std_string),
|
||||
automation.ApplyField(CONF_USERNAME, "set_username", cg.std_string),
|
||||
automation.ApplyField(CONF_URL, "set_url", cg.std_string),
|
||||
automation.ApplyCall("flash()"),
|
||||
)
|
||||
async def ota_http_request_action_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
if md5_url := config.get(CONF_MD5_URL):
|
||||
template_ = await cg.templatable(md5_url, args, cg.std_string)
|
||||
cg.add(var.set_md5_url(template_))
|
||||
|
||||
if md5_str := config.get(CONF_MD5):
|
||||
template_ = await cg.templatable(md5_str, args, cg.std_string)
|
||||
cg.add(var.set_md5(template_))
|
||||
|
||||
if password_str := config.get(CONF_PASSWORD):
|
||||
template_ = await cg.templatable(password_str, args, cg.std_string)
|
||||
cg.add(var.set_password(template_))
|
||||
|
||||
if username_str := config.get(CONF_USERNAME):
|
||||
template_ = await cg.templatable(username_str, args, cg.std_string)
|
||||
cg.add(var.set_username(template_))
|
||||
|
||||
template_ = await cg.templatable(config[CONF_URL], args, cg.std_string)
|
||||
cg.add(var.set_url(template_))
|
||||
|
||||
return var
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include "ota_http_request.h"
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome::http_request {
|
||||
|
||||
template<typename... Ts> class OtaHttpRequestComponentFlashAction final : public Action<Ts...> {
|
||||
public:
|
||||
OtaHttpRequestComponentFlashAction(OtaHttpRequestComponent *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(std::string, md5_url)
|
||||
TEMPLATABLE_VALUE(std::string, md5)
|
||||
TEMPLATABLE_VALUE(std::string, password)
|
||||
TEMPLATABLE_VALUE(std::string, url)
|
||||
TEMPLATABLE_VALUE(std::string, username)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
if (this->md5_url_.has_value()) {
|
||||
this->parent_->set_md5_url(this->md5_url_.value(x...));
|
||||
}
|
||||
if (this->md5_.has_value()) {
|
||||
this->parent_->set_md5(this->md5_.value(x...));
|
||||
}
|
||||
if (this->password_.has_value()) {
|
||||
this->parent_->set_password(this->password_.value(x...));
|
||||
}
|
||||
if (this->username_.has_value()) {
|
||||
this->parent_->set_username(this->username_.value(x...));
|
||||
}
|
||||
this->parent_->set_url(this->url_.value(x...));
|
||||
|
||||
this->parent_->flash();
|
||||
// Normally never reached due to reboot
|
||||
}
|
||||
|
||||
protected:
|
||||
OtaHttpRequestComponent *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::http_request
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "htu21d.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::htu21d {
|
||||
|
||||
@@ -112,11 +113,7 @@ void HTU21DComponent::set_heater(bool status) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
if (status) {
|
||||
raw_heater |= (1 << HTU21D_REG_HTRE_BIT);
|
||||
} else {
|
||||
raw_heater &= ~(1 << HTU21D_REG_HTRE_BIT);
|
||||
}
|
||||
assign_bits(raw_heater, 1 << HTU21D_REG_HTRE_BIT, status);
|
||||
if (this->write_register(HTU21D_WRITERHT_REG_CMD, &raw_heater, 1) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,7 @@ void I2SAudioSpeakerBase::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize volume control. When audio_dac is configured, this sets the DAC volume and mute state.
|
||||
// Initialize volume control. When audio_dac is configured, this sets every DAC's volume and mute state.
|
||||
// When no audio_dac is configured, this initializes software volume control.
|
||||
this->set_volume(this->volume_);
|
||||
this->set_mute_state(this->mute_state_);
|
||||
@@ -101,9 +101,7 @@ void I2SAudioSpeakerBase::loop() {
|
||||
this->state_ = speaker::STATE_RUNNING;
|
||||
#ifdef USE_AUDIO_DAC
|
||||
// The task raises TASK_RUNNING only after enabling the channel, so the clocks are already running
|
||||
if (this->audio_dac_ != nullptr) {
|
||||
this->audio_dac_->on_audio_started();
|
||||
}
|
||||
this->notify_audio_dacs_started_();
|
||||
#endif // USE_AUDIO_DAC
|
||||
}
|
||||
if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPING) {
|
||||
@@ -204,7 +202,7 @@ void I2SAudioSpeakerBase::set_mute_state(bool mute_state) {
|
||||
|
||||
void I2SAudioSpeakerBase::post_software_gain_(uint32_t rate_samples) {
|
||||
#ifdef USE_AUDIO_DAC
|
||||
if (this->audio_dac_ != nullptr) {
|
||||
if (this->has_audio_dac_()) {
|
||||
return; // Hardware volume; the ramp stays at unity
|
||||
}
|
||||
#endif // USE_AUDIO_DAC
|
||||
@@ -427,7 +425,7 @@ void I2SAudioSpeakerBase::drain_lockstep_(uint32_t extra_frames) {
|
||||
|
||||
void I2SAudioSpeakerBase::apply_software_volume_(uint8_t *data, size_t bytes_read) {
|
||||
#ifdef USE_AUDIO_DAC
|
||||
if (this->audio_dac_ != nullptr) {
|
||||
if (this->has_audio_dac_()) {
|
||||
return; // Hardware volume; the ramp is never targeted
|
||||
}
|
||||
#endif // USE_AUDIO_DAC
|
||||
|
||||
@@ -42,9 +42,7 @@ def _final_validate(config: dict[str, Any]) -> None:
|
||||
|
||||
# Get the transmitter configuration
|
||||
transmitter_id = config[CONF_REMOTE_TRANSMITTER_ID]
|
||||
full_config = fv.full_config.get()
|
||||
transmitter_path = full_config.get_path_for_id(transmitter_id)[:-1]
|
||||
transmitter_config = full_config.get_config_for_path(transmitter_path)
|
||||
transmitter_config = fv.get_declaring_config(transmitter_id)
|
||||
|
||||
# Check if carrier_duty_percent set to 0 or 100
|
||||
# Note: remote_transmitter schema requires this field and validates 1-100%,
|
||||
|
||||
@@ -35,11 +35,7 @@ def _final_validate(config: ConfigType) -> None:
|
||||
if CONF_REMOTE_TRANSMITTER_ID not in config:
|
||||
return
|
||||
|
||||
full_config = fv.full_config.get()
|
||||
transmitter_path = full_config.get_path_for_id(config[CONF_REMOTE_TRANSMITTER_ID])[
|
||||
:-1
|
||||
]
|
||||
transmitter_config = full_config.get_config_for_path(transmitter_path)
|
||||
transmitter_config = fv.get_declaring_config(config[CONF_REMOTE_TRANSMITTER_ID])
|
||||
|
||||
duty_percent = transmitter_config.get(CONF_CARRIER_DUTY_PERCENT)
|
||||
if duty_percent is not None and duty_percent != 100:
|
||||
|
||||
@@ -2,17 +2,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
from esphome.components.const import CONF_QUERY_PARAMS
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_ID,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_DATABASE,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
from esphome.const import CONF_FACTORY_RESET, CONF_ID, CONF_RESTART
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns
|
||||
@@ -24,23 +14,11 @@ RestartButton = ld2410_ns.class_("RestartButton", button.Button)
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
FactoryResetButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
RestartButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
),
|
||||
cv.Optional(CONF_QUERY_PARAMS): button.button_schema(
|
||||
QueryButton,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_DATABASE,
|
||||
cv.Optional(CONF_FACTORY_RESET): button.factory_reset_button_schema(
|
||||
FactoryResetButton
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.restart_button_schema(RestartButton),
|
||||
cv.Optional(CONF_QUERY_PARAMS): button.query_button_schema(QueryButton),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -195,17 +195,17 @@ void LD2410Component::dump_config() {
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
ESP_LOGCONFIG(TAG, "Sensors:");
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "Light", this->light_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "DetectionDistance", this->detection_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "MovingTargetDistance", this->moving_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "MovingTargetEnergy", this->moving_target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "StillTargetDistance", this->still_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "StillTargetEnergy", this->still_target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "Light", this->light_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "DetectionDistance", this->detection_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "MovingTargetDistance", this->moving_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "MovingTargetEnergy", this->moving_target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "StillTargetDistance", this->still_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "StillTargetEnergy", this->still_target_energy_sensor_);
|
||||
for (auto &s : this->gate_move_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "GateMove", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "GateMove", s);
|
||||
}
|
||||
for (auto &s : this->gate_still_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "GateStill", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "GateStill", s);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
||||
@@ -129,8 +129,8 @@ class LD2410Component final : public Component, public uart::UARTDevice {
|
||||
std::array<number::Number *, TOTAL_GATES> gate_still_threshold_numbers_{};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::array<SensorWithDedup<uint8_t>, TOTAL_GATES> gate_move_sensors_{};
|
||||
std::array<SensorWithDedup<uint8_t>, TOTAL_GATES> gate_still_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, TOTAL_GATES> gate_move_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, TOTAL_GATES> gate_still_sensors_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ void LD2410SComponent::dump_config() {
|
||||
LOG_BINARY_SENSOR(" ", "Has Target", this->target_binary_sensor_);
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "Distance", this->distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "Distance", this->distance_sensor_);
|
||||
#endif
|
||||
#ifdef USE_LD2410S_VERSION
|
||||
LOG_TEXT_SENSOR(" ", "Version", this->version_text_sensor_);
|
||||
|
||||
@@ -6,13 +6,8 @@ from esphome.const import (
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_ID,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_DATABASE,
|
||||
ICON_PULSE,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -29,23 +24,11 @@ CONF_START_DYNAMIC_BACKGROUND_CORRECTION = "start_dynamic_background_correction"
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
FactoryResetButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_QUERY_PARAMS): button.button_schema(
|
||||
QueryButton,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_DATABASE,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
RestartButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
cv.Optional(CONF_FACTORY_RESET): button.factory_reset_button_schema(
|
||||
FactoryResetButton
|
||||
),
|
||||
cv.Optional(CONF_QUERY_PARAMS): button.query_button_schema(QueryButton),
|
||||
cv.Optional(CONF_RESTART): button.restart_button_schema(RestartButton),
|
||||
cv.Optional(CONF_START_DYNAMIC_BACKGROUND_CORRECTION): button.button_schema(
|
||||
StartDynamicBackgroundCorrectionButton,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -17,23 +18,6 @@ namespace esphome::ld2412 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "ld2412");
|
||||
|
||||
enum BaudRate : uint8_t {
|
||||
BAUD_RATE_9600 = 1,
|
||||
BAUD_RATE_19200 = 2,
|
||||
BAUD_RATE_38400 = 3,
|
||||
BAUD_RATE_57600 = 4,
|
||||
BAUD_RATE_115200 = 5,
|
||||
BAUD_RATE_230400 = 6,
|
||||
BAUD_RATE_256000 = 7,
|
||||
BAUD_RATE_460800 = 8,
|
||||
};
|
||||
|
||||
enum DistanceResolution : uint8_t {
|
||||
DISTANCE_RESOLUTION_0_2 = 0x03,
|
||||
DISTANCE_RESOLUTION_0_5 = 0x01,
|
||||
DISTANCE_RESOLUTION_0_75 = 0x00,
|
||||
};
|
||||
|
||||
enum LightFunction : uint8_t {
|
||||
LIGHT_FUNCTION_OFF = 0x00,
|
||||
LIGHT_FUNCTION_BELOW = 0x01,
|
||||
@@ -82,77 +66,18 @@ enum AckData : uint8_t {
|
||||
ACK_PAYLOAD = 10,
|
||||
};
|
||||
|
||||
// Memory-efficient lookup tables
|
||||
struct StringToUint8 {
|
||||
const char *str;
|
||||
const uint8_t value;
|
||||
};
|
||||
// Index + 1 is the BaudRate value; the select options use the same order
|
||||
PROGMEM_STRING_TABLE(BaudRateStrings, "9600", "19200", "38400", "57600", "115200", "230400", "256000", "460800");
|
||||
static_assert(BAUD_RATE_9600 == 1 && BAUD_RATE_460800 == BaudRateStrings::COUNT, "BaudRate is index + 1");
|
||||
// Same order as DISTANCE_RESOLUTIONS
|
||||
PROGMEM_STRING_TABLE(DistanceResolutionStrings, "0.2m", "0.5m", "0.75m");
|
||||
// The light function select options are in enum order
|
||||
static_assert(LIGHT_FUNCTION_OFF == 0 && LIGHT_FUNCTION_BELOW == 1 && LIGHT_FUNCTION_ABOVE == 2,
|
||||
"LightFunction is the select option index");
|
||||
// Device values of the out pin level select options, in option order
|
||||
static constexpr uint8_t OUT_PIN_LEVELS[] PROGMEM = {OUT_PIN_LEVEL_LOW, OUT_PIN_LEVEL_HIGH};
|
||||
|
||||
struct Uint8ToString {
|
||||
const uint8_t value;
|
||||
const char *str;
|
||||
};
|
||||
|
||||
constexpr StringToUint8 BAUD_RATES_BY_STR[] = {
|
||||
{"9600", BAUD_RATE_9600}, {"19200", BAUD_RATE_19200}, {"38400", BAUD_RATE_38400},
|
||||
{"57600", BAUD_RATE_57600}, {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400},
|
||||
{"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800},
|
||||
};
|
||||
|
||||
constexpr StringToUint8 DISTANCE_RESOLUTIONS_BY_STR[] = {
|
||||
{"0.2m", DISTANCE_RESOLUTION_0_2},
|
||||
{"0.5m", DISTANCE_RESOLUTION_0_5},
|
||||
{"0.75m", DISTANCE_RESOLUTION_0_75},
|
||||
};
|
||||
|
||||
constexpr Uint8ToString DISTANCE_RESOLUTIONS_BY_UINT[] = {
|
||||
{DISTANCE_RESOLUTION_0_2, "0.2m"},
|
||||
{DISTANCE_RESOLUTION_0_5, "0.5m"},
|
||||
{DISTANCE_RESOLUTION_0_75, "0.75m"},
|
||||
};
|
||||
|
||||
constexpr StringToUint8 LIGHT_FUNCTIONS_BY_STR[] = {
|
||||
{"off", LIGHT_FUNCTION_OFF},
|
||||
{"below", LIGHT_FUNCTION_BELOW},
|
||||
{"above", LIGHT_FUNCTION_ABOVE},
|
||||
};
|
||||
|
||||
constexpr Uint8ToString LIGHT_FUNCTIONS_BY_UINT[] = {
|
||||
{LIGHT_FUNCTION_OFF, "off"},
|
||||
{LIGHT_FUNCTION_BELOW, "below"},
|
||||
{LIGHT_FUNCTION_ABOVE, "above"},
|
||||
};
|
||||
|
||||
constexpr StringToUint8 OUT_PIN_LEVELS_BY_STR[] = {
|
||||
{"low", OUT_PIN_LEVEL_LOW},
|
||||
{"high", OUT_PIN_LEVEL_HIGH},
|
||||
};
|
||||
|
||||
constexpr Uint8ToString OUT_PIN_LEVELS_BY_UINT[] = {
|
||||
{OUT_PIN_LEVEL_LOW, "low"},
|
||||
{OUT_PIN_LEVEL_HIGH, "high"},
|
||||
};
|
||||
|
||||
constexpr uint32_t BAUD_RATES[] = {9600, 19200, 38400, 57600, 115200, 230400, 256000, 460800};
|
||||
|
||||
// Helper functions for lookups
|
||||
template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const char *str) {
|
||||
for (const auto &entry : arr) {
|
||||
if (strcmp(str, entry.str) == 0) {
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
return 0xFF; // Not found
|
||||
}
|
||||
|
||||
template<size_t N> const char *find_str(const Uint8ToString (&arr)[N], uint8_t value) {
|
||||
for (const auto &entry : arr) {
|
||||
if (value == entry.value) {
|
||||
return entry.str;
|
||||
}
|
||||
}
|
||||
return ""; // Not found
|
||||
}
|
||||
static constexpr uint32_t BAUD_RATES[] PROGMEM = {9600, 19200, 38400, 57600, 115200, 230400, 256000, 460800};
|
||||
|
||||
static constexpr uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Default used when number component is not defined
|
||||
// Commands
|
||||
@@ -220,17 +145,17 @@ void LD2412Component::dump_config() {
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
ESP_LOGCONFIG(TAG, "Sensors:");
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "Light", this->light_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "DetectionDistance", this->detection_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "MovingTargetDistance", this->moving_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "MovingTargetEnergy", this->moving_target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "StillTargetDistance", this->still_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "StillTargetEnergy", this->still_target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "Light", this->light_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "DetectionDistance", this->detection_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "MovingTargetDistance", this->moving_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "MovingTargetEnergy", this->moving_target_energy_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "StillTargetDistance", this->still_target_distance_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "StillTargetEnergy", this->still_target_energy_sensor_);
|
||||
for (auto &s : this->gate_still_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "GateStill", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "GateStill", s);
|
||||
}
|
||||
for (auto &s : this->gate_move_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "GateMove", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "GateMove", s);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
@@ -524,11 +449,16 @@ void LD2412Component::handle_ack_data_() {
|
||||
if (this->buffer_pos_ < 11 + HEADER_FOOTER_SIZE) {
|
||||
break; // too short to hold the resolution
|
||||
}
|
||||
const auto *distance_resolution = find_str(DISTANCE_RESOLUTIONS_BY_UINT, this->buffer_data_[10]);
|
||||
ESP_LOGV(TAG, "Distance resolution: %s", distance_resolution);
|
||||
const auto index = ld24xx::find_index(DISTANCE_RESOLUTIONS, this->buffer_data_[10]);
|
||||
if (!index) {
|
||||
ESP_LOGW(TAG, "Unknown distance resolution %u", this->buffer_data_[10]);
|
||||
break;
|
||||
}
|
||||
ESP_LOGV(TAG, "Distance resolution: %s",
|
||||
LOG_STR_ARG(DistanceResolutionStrings::get_log_str(*index, DistanceResolutionStrings::LAST_INDEX)));
|
||||
#ifdef USE_SELECT
|
||||
if (this->distance_resolution_select_ != nullptr) {
|
||||
this->distance_resolution_select_->publish_state(distance_resolution);
|
||||
this->distance_resolution_select_->publish_state(*index);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
@@ -540,11 +470,10 @@ void LD2412Component::handle_ack_data_() {
|
||||
}
|
||||
this->light_function_ = this->buffer_data_[10];
|
||||
this->light_threshold_ = this->buffer_data_[11];
|
||||
const auto *light_function_str = find_str(LIGHT_FUNCTIONS_BY_UINT, this->light_function_);
|
||||
ESP_LOGV(TAG, "Light function: %s, threshold: %u", light_function_str, this->light_threshold_);
|
||||
ESP_LOGV(TAG, "Light function: %u, threshold: %u", this->light_function_, this->light_threshold_);
|
||||
#ifdef USE_SELECT
|
||||
if (this->light_function_select_ != nullptr) {
|
||||
this->light_function_select_->publish_state(light_function_str);
|
||||
this->light_function_select_->publish_state(static_cast<size_t>(this->light_function_));
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
@@ -661,9 +590,10 @@ void LD2412Component::handle_ack_data_() {
|
||||
*/
|
||||
this->out_pin_level_ = this->buffer_data_[14];
|
||||
#ifdef USE_SELECT
|
||||
const auto *out_pin_level_str = find_str(OUT_PIN_LEVELS_BY_UINT, this->out_pin_level_);
|
||||
if (this->out_pin_level_select_ != nullptr) {
|
||||
this->out_pin_level_select_->publish_state(out_pin_level_str);
|
||||
if (const auto index = ld24xx::find_index(OUT_PIN_LEVELS, this->out_pin_level_)) {
|
||||
this->out_pin_level_select_->publish_state(*index);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -717,15 +647,29 @@ void LD2412Component::set_bluetooth(bool enable) {
|
||||
}
|
||||
|
||||
void LD2412Component::set_distance_resolution(const char *state) {
|
||||
const int16_t index = progmem_string_table_find<DistanceResolutionStrings>(state, strlen(state));
|
||||
if (index >= 0) {
|
||||
this->set_distance_resolution(static_cast<DistanceResolution>(progmem_read_byte(&DISTANCE_RESOLUTIONS[index])));
|
||||
}
|
||||
}
|
||||
|
||||
void LD2412Component::set_distance_resolution(DistanceResolution resolution) {
|
||||
this->set_config_mode_(true);
|
||||
const uint8_t cmd_value[6] = {find_uint8(DISTANCE_RESOLUTIONS_BY_STR, state), 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const uint8_t cmd_value[6] = {resolution, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, sizeof(cmd_value));
|
||||
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
|
||||
}
|
||||
|
||||
void LD2412Component::set_baud_rate(const char *state) {
|
||||
const int16_t index = progmem_string_table_find<BaudRateStrings>(state, strlen(state));
|
||||
if (index >= 0) {
|
||||
this->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
void LD2412Component::set_baud_rate(BaudRate rate) {
|
||||
this->set_config_mode_(true);
|
||||
const uint8_t cmd_value[2] = {find_uint8(BAUD_RATES_BY_STR, state), 0x00};
|
||||
const uint8_t cmd_value[2] = {rate, 0x00};
|
||||
this->send_command_(CMD_SET_BAUD_RATE, cmd_value, sizeof(cmd_value));
|
||||
this->set_timeout(200, [this]() { this->restart_(); });
|
||||
}
|
||||
@@ -807,9 +751,10 @@ void LD2412Component::set_basic_config() {
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
if (this->out_pin_level_select_ != nullptr) {
|
||||
if (!this->out_pin_level_select_->has_state())
|
||||
const auto index = this->out_pin_level_select_->active_index();
|
||||
if (!index)
|
||||
return;
|
||||
out_pin_level = find_uint8(OUT_PIN_LEVELS_BY_STR, this->out_pin_level_select_->current_option().c_str());
|
||||
out_pin_level = progmem_read_byte(&OUT_PIN_LEVELS[*index]);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -881,8 +826,10 @@ void LD2412Component::set_light_out_control() {
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) {
|
||||
this->light_function_ = find_uint8(LIGHT_FUNCTIONS_BY_STR, this->light_function_select_->current_option().c_str());
|
||||
if (this->light_function_select_ != nullptr) {
|
||||
if (const auto index = this->light_function_select_->active_index()) {
|
||||
this->light_function_ = *index;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
uint8_t value[2] = {this->light_function_, this->light_threshold_};
|
||||
|
||||
@@ -36,6 +36,27 @@ using namespace ld24xx;
|
||||
static constexpr uint8_t MAX_LINE_LENGTH = 54; // Max characters for serial buffer
|
||||
static constexpr uint8_t TOTAL_GATES = 14; // Total number of gates supported by the LD2412
|
||||
|
||||
enum BaudRate : uint8_t {
|
||||
BAUD_RATE_9600 = 1,
|
||||
BAUD_RATE_19200 = 2,
|
||||
BAUD_RATE_38400 = 3,
|
||||
BAUD_RATE_57600 = 4,
|
||||
BAUD_RATE_115200 = 5,
|
||||
BAUD_RATE_230400 = 6,
|
||||
BAUD_RATE_256000 = 7,
|
||||
BAUD_RATE_460800 = 8,
|
||||
};
|
||||
|
||||
enum DistanceResolution : uint8_t {
|
||||
DISTANCE_RESOLUTION_0_2 = 0x03,
|
||||
DISTANCE_RESOLUTION_0_5 = 0x01,
|
||||
DISTANCE_RESOLUTION_0_75 = 0x00,
|
||||
};
|
||||
|
||||
// Device values of the distance resolution select options, in option order; in flash on ESP8266
|
||||
static constexpr uint8_t DISTANCE_RESOLUTIONS[] PROGMEM = {DISTANCE_RESOLUTION_0_2, DISTANCE_RESOLUTION_0_5,
|
||||
DISTANCE_RESOLUTION_0_75};
|
||||
|
||||
class LD2412Component final : public Component, public uart::UARTDevice {
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
SUB_BINARY_SENSOR(dynamic_background_correction_status)
|
||||
@@ -103,7 +124,9 @@ class LD2412Component final : public Component, public uart::UARTDevice {
|
||||
void restart_and_read_all_info();
|
||||
void set_bluetooth(bool enable);
|
||||
void set_distance_resolution(const char *state);
|
||||
void set_distance_resolution(DistanceResolution resolution);
|
||||
void set_baud_rate(const char *state);
|
||||
void set_baud_rate(BaudRate rate);
|
||||
void factory_reset();
|
||||
void start_dynamic_background_correction();
|
||||
|
||||
@@ -149,8 +172,8 @@ class LD2412Component final : public Component, public uart::UARTDevice {
|
||||
GateThresholds gate_still_thresholds_;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::array<SensorWithDedup<uint8_t>, TOTAL_GATES> gate_move_sensors_{};
|
||||
std::array<SensorWithDedup<uint8_t>, TOTAL_GATES> gate_still_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, TOTAL_GATES> gate_move_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, TOTAL_GATES> gate_still_sensors_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ CONFIG_SCHEMA = {
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_LD2412_ID])
|
||||
selects = select.sub_selects(config, parent=hub)
|
||||
# Option lists follow the order of the C++ enums and tables; the hub maps by index
|
||||
await selects(
|
||||
CONF_BAUD_RATE,
|
||||
hub.set_baud_rate_select,
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace esphome::ld2412 {
|
||||
|
||||
void BaudRateSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_baud_rate(this->option_at(index));
|
||||
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -4,7 +4,8 @@ namespace esphome::ld2412 {
|
||||
|
||||
void DistanceResolutionSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_distance_resolution(this->option_at(index));
|
||||
this->parent_->set_distance_resolution(
|
||||
static_cast<DistanceResolution>(progmem_read_byte(&DISTANCE_RESOLUTIONS[index])));
|
||||
}
|
||||
|
||||
} // namespace esphome::ld2412
|
||||
@@ -1,16 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_ID,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
from esphome.const import CONF_FACTORY_RESET, CONF_ID, CONF_RESTART
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
||||
@@ -21,18 +12,10 @@ RestartButton = ld2450_ns.class_("RestartButton", button.Button)
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
|
||||
cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
FactoryResetButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
RestartButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
cv.Optional(CONF_FACTORY_RESET): button.factory_reset_button_schema(
|
||||
FactoryResetButton
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.restart_button_schema(RestartButton),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -154,35 +154,35 @@ void LD2450Component::dump_config() {
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
ESP_LOGCONFIG(TAG, "Sensors:");
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "MovingTargetCount", this->moving_target_count_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "StillTargetCount", this->still_target_count_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetCount", this->target_count_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "MovingTargetCount", this->moving_target_count_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "StillTargetCount", this->still_target_count_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetCount", this->target_count_sensor_);
|
||||
for (auto &s : this->move_x_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetX", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetX", s);
|
||||
}
|
||||
for (auto &s : this->move_y_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetY", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetY", s);
|
||||
}
|
||||
for (auto &s : this->move_angle_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetAngle", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetAngle", s);
|
||||
}
|
||||
for (auto &s : this->move_distance_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetDistance", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetDistance", s);
|
||||
}
|
||||
for (auto &s : this->move_resolution_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetResolution", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetResolution", s);
|
||||
}
|
||||
for (auto &s : this->move_speed_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetSpeed", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetSpeed", s);
|
||||
}
|
||||
for (auto &s : this->zone_target_count_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "ZoneTargetCount", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "ZoneTargetCount", s);
|
||||
}
|
||||
for (auto &s : this->zone_moving_target_count_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "ZoneMovingTargetCount", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "ZoneMovingTargetCount", s);
|
||||
}
|
||||
for (auto &s : this->zone_still_target_count_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "ZoneStillTargetCount", s);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "ZoneStillTargetCount", s);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
||||
@@ -213,15 +213,15 @@ class LD2450Component : public Component, public uart::UARTDevice {
|
||||
ZoneOfNumbers zone_numbers_[MAX_ZONES];
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
std::array<SensorWithDedup<int16_t>, MAX_TARGETS> move_x_sensors_{};
|
||||
std::array<SensorWithDedup<int16_t>, MAX_TARGETS> move_y_sensors_{};
|
||||
std::array<SensorWithDedup<int16_t>, MAX_TARGETS> move_speed_sensors_{};
|
||||
std::array<SensorWithDedup<float>, MAX_TARGETS> move_angle_sensors_{};
|
||||
std::array<SensorWithDedup<uint16_t>, MAX_TARGETS> move_distance_sensors_{};
|
||||
std::array<SensorWithDedup<uint16_t>, MAX_TARGETS> move_resolution_sensors_{};
|
||||
std::array<SensorWithDedup<uint8_t>, MAX_ZONES> zone_target_count_sensors_{};
|
||||
std::array<SensorWithDedup<uint8_t>, MAX_ZONES> zone_still_target_count_sensors_{};
|
||||
std::array<SensorWithDedup<uint8_t>, MAX_ZONES> zone_moving_target_count_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<int16_t>, MAX_TARGETS> move_x_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<int16_t>, MAX_TARGETS> move_y_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<int16_t>, MAX_TARGETS> move_speed_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<float>, MAX_TARGETS> move_angle_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint16_t>, MAX_TARGETS> move_distance_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint16_t>, MAX_TARGETS> move_resolution_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, MAX_ZONES> zone_target_count_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, MAX_ZONES> zone_still_target_count_sensors_{};
|
||||
std::array<sensor::SensorWithDedup<uint8_t>, MAX_ZONES> zone_moving_target_count_sensors_{};
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
std::array<text_sensor::TextSensor *, MAX_TARGETS> direction_text_sensors_{};
|
||||
|
||||
@@ -71,12 +71,12 @@ void LD2460Component::dump_config() {
|
||||
: this->installation_mode_ == InstallationMode::INSTALLATION_MODE_TOP ? LOG_STR_LITERAL("top")
|
||||
: LOG_STR_LITERAL("side"));
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetCount", this->target_count_sensor_);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetCount", this->target_count_sensor_);
|
||||
for (const auto &t : this->target_sensors_) {
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetX", t.x);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetY", t.y);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetDistance", t.distance);
|
||||
LOG_SENSOR_WITH_DEDUP_SAFE(" ", "TargetAngle", t.angle);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetX", t.x);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetY", t.y);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetDistance", t.distance);
|
||||
LOG_SENSOR_WITH_DEDUP(" ", "TargetAngle", t.angle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class LD2460Component : public Component, public uart::UARTDevice {
|
||||
#ifdef USE_SENSOR
|
||||
// INT16_MIN marks a slot with no published coordinates, so the next report always publishes
|
||||
struct TargetSensors {
|
||||
ld24xx::SensorWithDedup<float> x, y, distance, angle;
|
||||
sensor::SensorWithDedup<float> x, y, distance, angle;
|
||||
int16_t last_x{INT16_MIN};
|
||||
int16_t last_y{INT16_MIN};
|
||||
};
|
||||
|
||||
@@ -14,11 +14,6 @@
|
||||
#include "esphome/components/sensor/sensor_with_dedup.h"
|
||||
#endif
|
||||
|
||||
#define LOG_SENSOR_WITH_DEDUP_SAFE(tag, name, sensor) \
|
||||
if ((sensor).has_sensor()) { \
|
||||
LOG_SENSOR(tag, name, (sensor).get_sensor()); \
|
||||
}
|
||||
|
||||
#define SAFE_PUBLISH_SENSOR(sensor, value) (sensor).publish_state_if_not_dup(value)
|
||||
|
||||
#define SAFE_PUBLISH_SENSOR_UNKNOWN(sensor) (sensor).publish_state_unknown()
|
||||
@@ -129,7 +124,4 @@ void feed_frame_byte(uint8_t (&buffer)[N], uint8_t &pos, uint8_t byte, const uin
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
template<typename T> using SensorWithDedup = sensor::SensorWithDedup<T>;
|
||||
#endif
|
||||
} // namespace esphome::ld24xx
|
||||
@@ -101,8 +101,7 @@ def final_validate(config: ConfigType) -> None:
|
||||
)
|
||||
|
||||
if config.get(CONF_WAKE):
|
||||
hub_path = full_config.get_path_for_id(hub_id)
|
||||
hub_config = full_config.get_config_for_path(hub_path[:-1])
|
||||
hub_config = fv.get_declaring_config(hub_id, full_config)
|
||||
if hub_config.get(CONF_WAKEUP_PIN) is None:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_WAKE} requires {CONF_WAKEUP_PIN} on the parent ld6002b component",
|
||||
|
||||
@@ -57,6 +57,9 @@ from .const import (
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
# Fault handlers the crash handler wraps (BK7238, BK7231N)
|
||||
BK_TRAP_HANDLERS = ("bk_trap_udef", "bk_trap_pabt", "bk_trap_dabt")
|
||||
|
||||
CODEOWNERS = ["@kuba2k2"]
|
||||
AUTO_LOAD = ["preferences"]
|
||||
DOMAIN = "libretiny"
|
||||
@@ -551,6 +554,13 @@ async def component_to_code(config):
|
||||
# RAM-executable output section and prints a post-link placement summary.
|
||||
if FAMILY_COMPONENT[config[CONF_FAMILY]] != COMPONENT_BK72XX:
|
||||
cg.add_platformio_option("extra_scripts", ["pre:patch_linker.py"])
|
||||
if config[CONF_FAMILY] in (FAMILY_BK7231N, FAMILY_BK7238):
|
||||
# Crash handler (crash_handler.cpp, patch_noinit.py.script)
|
||||
cg.add_define("USE_LIBRETINY_CRASH_HANDLER")
|
||||
cg.add_define("USE_CRASH_HANDLER")
|
||||
for trap in BK_TRAP_HANDLERS:
|
||||
cg.add_build_flag(f"-Wl,--wrap={trap}")
|
||||
cg.add_platformio_option("extra_scripts", ["pre:patch_noinit.py"])
|
||||
# Match the 1-byte enums of the LN882H SDK's prebuilt WiFi library.
|
||||
if FAMILY_COMPONENT[config[CONF_FAMILY]] == COMPONENT_LN882X:
|
||||
cg.add_build_unflag("-fno-short-enums")
|
||||
@@ -658,4 +668,8 @@ def copy_files() -> None:
|
||||
script_dir / "scons_dont_inherit.py.script",
|
||||
CORE.relative_build_path("scons_dont_inherit.py"),
|
||||
)
|
||||
copy_file_if_changed(
|
||||
script_dir / "patch_noinit.py.script",
|
||||
CORE.relative_build_path("patch_noinit.py"),
|
||||
)
|
||||
copy_ccache_script()
|
||||
@@ -0,0 +1,217 @@
|
||||
#ifdef USE_BK72XX
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_LIBRETINY_CRASH_HANDLER
|
||||
|
||||
#include "crash_handler.h"
|
||||
#include "core.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" {
|
||||
#include <arm_mcu_pub.h> // MCU_REG_BACKUP_SP_*
|
||||
#include <intc.h> // struct arm_registers
|
||||
}
|
||||
|
||||
// On a fault the SDK (boot_handlers.S, PUSH_SVC_REG) builds a struct arm_registers frame and branches
|
||||
// to bk_trap_udef/pabt/dabt, which record the crash type, print a dump sdk_silent hides and spin for
|
||||
// the watchdog. The wrappers (-Wl,--wrap) first save the fault location and a stack scan for the
|
||||
// next boot. pc is the exception LR; PUSH_SVC_REG stores pc and spsr at these word offsets.
|
||||
static_assert(sizeof(arm_registers) == 18 * sizeof(uint32_t), "SDK exception frame changed size");
|
||||
static_assert(offsetof(arm_registers, pc) == 15 * 4 && offsetof(arm_registers, spsr) == 16 * 4,
|
||||
"SDK exception frame layout changed");
|
||||
static_assert(LT_HW_WATCHDOG, "the crash reboot relies on the hardware watchdog");
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
extern "C" {
|
||||
void __real_bk_trap_udef(arm_registers *regs);
|
||||
void __real_bk_trap_pabt(arm_registers *regs);
|
||||
void __real_bk_trap_dabt(arm_registers *regs);
|
||||
// Bounds of the app's flash image and its ITCM code (linker symbols).
|
||||
extern char _vector_start[];
|
||||
extern char _data_flash_begin[];
|
||||
extern char _itcmcode_ram_begin[];
|
||||
extern char _itcmcode_ram_end[];
|
||||
}
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
|
||||
namespace esphome::libretiny {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "libretiny.crash");
|
||||
|
||||
// 0xDEAD sentinel, then the record layout version.
|
||||
static constexpr uint32_t CRASH_MAGIC_V1 = 0xDEAD0001;
|
||||
|
||||
static constexpr uint32_t CRASH_REASON_UNDEFINED = 1;
|
||||
static constexpr uint32_t CRASH_REASON_PREFETCH_ABORT = 2;
|
||||
static constexpr uint32_t CRASH_REASON_DATA_ABORT = 3;
|
||||
PROGMEM_STRING_TABLE(CrashReasonStrings, "Unknown", "Undefined instruction", "Prefetch abort", "Data abort");
|
||||
|
||||
// With Wi-Fi up the boot-time watchdog no longer fires: any interrupts-off spin hangs until power is
|
||||
// cycled (BK7238 and BK7231N; cause not in the open SDK). Re-armed here, long enough for the SDK's
|
||||
// register dump when sdk_silent is off.
|
||||
static constexpr uint32_t CRASH_REBOOT_WDT_MS = 500;
|
||||
|
||||
static constexpr size_t MAX_BACKTRACE = 8;
|
||||
// Words scanned above sp for return addresses.
|
||||
static constexpr size_t STACK_SCAN_WORDS = 64;
|
||||
|
||||
// SRAM: 192 KB on the BK7231N, 256 KB on the BK7238.
|
||||
static constexpr uint32_t SRAM_START = 0x00400000;
|
||||
#ifdef USE_LIBRETINY_VARIANT_BK7231N
|
||||
static constexpr uint32_t SRAM_END = 0x00430000;
|
||||
#else
|
||||
static constexpr uint32_t SRAM_END = 0x00440000;
|
||||
#endif
|
||||
|
||||
static constexpr uint32_t CPSR_MODE_MASK = 0x1F;
|
||||
static constexpr uint32_t CPSR_MODE_USR = 0x10;
|
||||
static constexpr uint32_t CPSR_MODE_FIQ = 0x11;
|
||||
static constexpr uint32_t CPSR_MODE_IRQ = 0x12;
|
||||
static constexpr uint32_t CPSR_MODE_SVC = 0x13;
|
||||
static constexpr uint32_t CPSR_MODE_ABT = 0x17;
|
||||
static constexpr uint32_t CPSR_MODE_UND = 0x1B;
|
||||
static constexpr uint32_t CPSR_MODE_SYS = 0x1F;
|
||||
static constexpr uint32_t CPSR_THUMB = 1u << 5;
|
||||
|
||||
struct CrashRecord {
|
||||
uint32_t magic;
|
||||
uint32_t reason;
|
||||
uint32_t pc;
|
||||
uint32_t lr;
|
||||
uint32_t sp;
|
||||
uint32_t backtrace[MAX_BACKTRACE];
|
||||
};
|
||||
|
||||
// Kept in ITCM, see patch_noinit.py.script.
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
__attribute__((section(".noinit"))) static CrashRecord s_record;
|
||||
|
||||
static bool s_record_read = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_record_valid = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
bool crash_handler_has_data() {
|
||||
if (!s_record_read) {
|
||||
s_record_read = true;
|
||||
s_record_valid = s_record.magic == CRASH_MAGIC_V1;
|
||||
// Clear the magic so the record is reported once.
|
||||
s_record.magic = 0;
|
||||
}
|
||||
return s_record_valid;
|
||||
}
|
||||
|
||||
// One ESP_LOGE per line: the CLI decodes per line and partial output still shows.
|
||||
void crash_handler_log() {
|
||||
if (!crash_handler_has_data())
|
||||
return;
|
||||
|
||||
ESP_LOGE(TAG, "*** CRASH DETECTED ON PREVIOUS BOOT ***");
|
||||
ESP_LOGE(TAG, " Reason: %s", LOG_STR_ARG(CrashReasonStrings::get_log_str(s_record.reason, 0)));
|
||||
ESP_LOGE(TAG, " PC: 0x%08" PRIX32 " (fault location)", s_record.pc);
|
||||
ESP_LOGE(TAG, " LR: 0x%08" PRIX32 " (return address)", s_record.lr);
|
||||
ESP_LOGE(TAG, " SP: 0x%08" PRIX32, s_record.sp);
|
||||
uint8_t count = 0;
|
||||
for (; count < MAX_BACKTRACE && s_record.backtrace[count] != 0; count++) {
|
||||
ESP_LOGE(TAG, " BT%d: 0x%08" PRIX32 " (stack backtrace)", count, s_record.backtrace[count]);
|
||||
}
|
||||
// addr2line hint with every address
|
||||
char hint[160];
|
||||
int pos = snprintf(hint, sizeof(hint), "Use: addr2line -pfiaC -e raw_firmware.elf 0x%08" PRIX32 " 0x%08" PRIX32,
|
||||
s_record.pc, s_record.lr);
|
||||
for (uint8_t i = 0; i < count && pos < (int) sizeof(hint) - 12; i++) {
|
||||
pos += snprintf(hint + pos, sizeof(hint) - pos, " 0x%08" PRIX32, s_record.backtrace[i]);
|
||||
}
|
||||
ESP_LOGE(TAG, "%s", hint);
|
||||
}
|
||||
|
||||
// --- Crash time: exception mode, interrupts masked, a few dozen bytes of stack: no printing, no
|
||||
// large locals.
|
||||
|
||||
static inline bool is_code_addr(uint32_t val) {
|
||||
uint32_t addr = val & ~1u; // clear the Thumb bit of a return address
|
||||
return (addr >= (uint32_t) _vector_start && addr < (uint32_t) _data_flash_begin) ||
|
||||
(addr >= (uint32_t) _itcmcode_ram_begin && addr < (uint32_t) _itcmcode_ram_end);
|
||||
}
|
||||
|
||||
// PUSH_ALL_ARM_REG (boot_handlers.S) saves every mode's registers at the arm_mcu_pub.h addresses
|
||||
// before bk_trap_*; lr (r14) follows sp (r13).
|
||||
struct SavedBank {
|
||||
uint32_t mode;
|
||||
uint32_t sp_addr;
|
||||
};
|
||||
static constexpr SavedBank SAVED_BANKS[] = {
|
||||
{CPSR_MODE_SYS, MCU_REG_BACKUP_SP_SYS}, {CPSR_MODE_IRQ, MCU_REG_BACKUP_SP_IRQ},
|
||||
{CPSR_MODE_FIQ, MCU_REG_BACKUP_SP_FIQ}, {CPSR_MODE_ABT, MCU_REG_BACKUP_SP_ABT},
|
||||
{CPSR_MODE_UND, MCU_REG_BACKUP_SP_UND}, {CPSR_MODE_SVC, MCU_REG_BACKUP_SP_SVC},
|
||||
};
|
||||
|
||||
// sp and lr stay 0 for an unknown mode (corrupt spsr).
|
||||
static void read_saved_sp_lr(uint32_t mode, uint32_t *sp, uint32_t *lr) {
|
||||
for (const auto &bank : SAVED_BANKS) {
|
||||
if (bank.mode != mode)
|
||||
continue;
|
||||
const auto *regs = (const volatile uint32_t *) bank.sp_addr; // NOLINT(performance-no-int-to-ptr)
|
||||
*sp = regs[0];
|
||||
*lr = regs[1];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void record_crash(arm_registers *regs, uint32_t reason, uint32_t pc_offset) {
|
||||
uint32_t mode = regs->spsr & CPSR_MODE_MASK;
|
||||
if (mode == CPSR_MODE_USR)
|
||||
mode = CPSR_MODE_SYS; // same register bank
|
||||
uint32_t sp = 0;
|
||||
uint32_t lr = 0;
|
||||
read_saved_sp_lr(mode, &sp, &lr);
|
||||
|
||||
s_record.reason = reason;
|
||||
s_record.pc = regs->pc - pc_offset;
|
||||
s_record.lr = lr;
|
||||
s_record.sp = sp;
|
||||
|
||||
// Return addresses above sp give the backtrace.
|
||||
uint32_t count = 0;
|
||||
if (sp >= SRAM_START && sp < SRAM_END && (sp & 3) == 0) {
|
||||
auto *scan = (const uint32_t *) sp; // NOLINT(performance-no-int-to-ptr)
|
||||
auto *end = (const uint32_t *) SRAM_END; // NOLINT(performance-no-int-to-ptr)
|
||||
for (size_t i = 0; i < STACK_SCAN_WORDS && scan < end && count < MAX_BACKTRACE; i++, scan++) {
|
||||
uint32_t val = *scan;
|
||||
if (is_code_addr(val) && val != lr && (val & ~1u) != s_record.pc)
|
||||
s_record.backtrace[count++] = val;
|
||||
}
|
||||
}
|
||||
if (count < MAX_BACKTRACE)
|
||||
s_record.backtrace[count] = 0; // terminator, the reader stops at the first 0
|
||||
s_record.magic = CRASH_MAGIC_V1;
|
||||
|
||||
lt_wdt_enable(CRASH_REBOOT_WDT_MS);
|
||||
}
|
||||
|
||||
// The exception LR is past the fault: one instruction (undefined), 4 bytes (prefetch), 8 (data abort).
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
extern "C" void __wrap_bk_trap_udef(arm_registers *regs) {
|
||||
record_crash(regs, CRASH_REASON_UNDEFINED, (regs->spsr & CPSR_THUMB) ? 2 : 4);
|
||||
__real_bk_trap_udef(regs);
|
||||
}
|
||||
|
||||
extern "C" void __wrap_bk_trap_pabt(arm_registers *regs) {
|
||||
record_crash(regs, CRASH_REASON_PREFETCH_ABORT, 4);
|
||||
__real_bk_trap_pabt(regs);
|
||||
}
|
||||
|
||||
extern "C" void __wrap_bk_trap_dabt(arm_registers *regs) {
|
||||
record_crash(regs, CRASH_REASON_DATA_ABORT, 8);
|
||||
__real_bk_trap_dabt(regs);
|
||||
}
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
|
||||
|
||||
} // namespace esphome::libretiny
|
||||
|
||||
#endif // USE_LIBRETINY_CRASH_HANDLER
|
||||
#endif // USE_BK72XX
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_LIBRETINY_CRASH_HANDLER
|
||||
|
||||
namespace esphome::libretiny {
|
||||
|
||||
/// Log crash data if a crash was detected on previous boot.
|
||||
void crash_handler_log();
|
||||
|
||||
/// Returns true if crash data was found this boot, reading it first if needed.
|
||||
bool crash_handler_has_data();
|
||||
|
||||
} // namespace esphome::libretiny
|
||||
|
||||
#endif // USE_LIBRETINY_CRASH_HANDLER
|
||||
#endif // USE_BK72XX
|
||||
@@ -149,6 +149,11 @@ COMPONENT_CODEOWNERS = {
|
||||
"ln882x": ["@lamauny"],
|
||||
}
|
||||
|
||||
# Extra imports per component; the CLI looks up process_stacktrace on the platform package.
|
||||
COMPONENT_EXTRA_IMPORTS = {
|
||||
"bk72xx": "from esphome.stacktrace import process_crash_handler_line as process_stacktrace # noqa: F401",
|
||||
}
|
||||
|
||||
|
||||
def subst(code: str, key: str, value: str) -> str:
|
||||
return code.replace(f"{{{key}}}", value)
|
||||
@@ -225,6 +230,8 @@ def write_component_code(
|
||||
import_lines = "\n".join(
|
||||
f"from .{m} import {', '.join(sorted(v))}" for m, v in imports.items() if v
|
||||
)
|
||||
if extra := COMPONENT_EXTRA_IMPORTS.get(component):
|
||||
import_lines = f"{extra}\n{import_lines}"
|
||||
code = subst_many(
|
||||
code,
|
||||
("IMPORTS", import_lines),
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# pylint: disable=E0602
|
||||
Import("env") # noqa
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
# Adds a .noinit section for the crash record (crash_handler.cpp) to the
|
||||
# BK7238 / BK7231N linker script, in ITCM after .itcm.code: ITCM survives
|
||||
# the watchdog reset and the SDK startup only copies .itcm.code there, while
|
||||
# the bootloader clears at least 128 KB of SRAM and the SDK's saved registers
|
||||
# on every boot (measured on a BK7238). The LMA stays in ITCM. BK7231T/Q/7251
|
||||
# have no ITCM region, so libretiny/__init__.py loads this for two families.
|
||||
|
||||
_MARKER = "/* esphome .noinit */"
|
||||
_ITCM_CODE_END = re.compile(r"(\}\s*>\s*itcm\s+AT\s*>\s*flash[^\n]*\n)")
|
||||
_NOINIT_BLOCK = (
|
||||
"\t.noinit (NOLOAD) : " + _MARKER + "\n"
|
||||
"\t{\n"
|
||||
"\t\t. = ALIGN(8);\n"
|
||||
"\t\tKEEP(*(.noinit .noinit.*))\n"
|
||||
"\t} > itcm AT> itcm\n"
|
||||
)
|
||||
|
||||
|
||||
def _pre_link(target, source, env):
|
||||
build_dir = env.subst("$BUILD_DIR")
|
||||
found = False
|
||||
for name in os.listdir(build_dir):
|
||||
if not name.endswith(".ld"):
|
||||
continue
|
||||
path = os.path.join(build_dir, name)
|
||||
with open(path, "r", encoding="utf-8") as fh:
|
||||
content = fh.read()
|
||||
if _MARKER in content:
|
||||
found = True
|
||||
continue
|
||||
new_content = _ITCM_CODE_END.sub(
|
||||
lambda m: m.group(1) + _NOINIT_BLOCK, content, count=1
|
||||
)
|
||||
if new_content == content:
|
||||
continue
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
fh.write(new_content)
|
||||
print("ESPHome: patched {} (.noinit section)".format(name))
|
||||
found = True
|
||||
if not found:
|
||||
raise RuntimeError(
|
||||
"ESPHome: no .ld in {} got the .noinit section; the crash record "
|
||||
"would not survive a reboot. Update the regex in "
|
||||
"patch_noinit.py.script for this LibreTiny version.".format(build_dir)
|
||||
)
|
||||
|
||||
|
||||
ITCM_START = 0x003F0000
|
||||
ITCM_END = 0x00400000
|
||||
|
||||
|
||||
def _post_link(target, source, env):
|
||||
"""Fail unless the linked ELF keeps .noinit in ITCM (stale marker, renamed script)."""
|
||||
elf = env.subst("$BUILD_DIR/${PROGNAME}.elf")
|
||||
objdump = re.sub(r"(gcc-)?nm$", "objdump", env.subst("$NM")) # same toolchain prefix
|
||||
headers = subprocess.check_output([objdump, "-h", elf], text=True)
|
||||
for line in headers.splitlines():
|
||||
fields = line.split()
|
||||
if len(fields) > 3 and fields[1] == ".noinit":
|
||||
vma = int(fields[3], 16)
|
||||
if ITCM_START <= vma < ITCM_END:
|
||||
return
|
||||
raise RuntimeError(
|
||||
"ESPHome: .noinit landed at 0x{:08x}, outside ITCM; the crash "
|
||||
"record would not survive a reboot.".format(vma)
|
||||
)
|
||||
raise RuntimeError("ESPHome: the linked ELF has no .noinit section.")
|
||||
|
||||
|
||||
# LibreTiny renders the .ld into $BUILD_DIR after this script loads; patch at link time.
|
||||
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", _pre_link)
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", _post_link)
|
||||
@@ -380,8 +380,7 @@ def _final_validate(config: ConfigType) -> None:
|
||||
|
||||
for ref in refs:
|
||||
try:
|
||||
light_path = fconf.get_path_for_id(ref.light_id)[:-1]
|
||||
light_config = fconf.get_config_for_path(light_path)
|
||||
light_config = fv.get_declaring_config(ref.light_id, fconf)
|
||||
except KeyError:
|
||||
# Light ID not found — ID validation will have already reported this
|
||||
continue
|
||||
@@ -398,8 +397,7 @@ def _final_validate(config: ConfigType) -> None:
|
||||
|
||||
for ref in cycle_refs:
|
||||
try:
|
||||
light_path = fconf.get_path_for_id(ref.light_id)[:-1]
|
||||
light_config = fconf.get_config_for_path(light_path)
|
||||
light_config = fv.get_declaring_config(ref.light_id, fconf)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ from esphome.const import (
|
||||
)
|
||||
from esphome.core import CORE, ID, EsphomeError, Lambda
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
import esphome.final_validate as fv
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -253,8 +254,7 @@ def _resolve_effect_index(config: ConfigType, original_name: str) -> int:
|
||||
from . import effect_not_found_message, find_effect_index
|
||||
|
||||
light_id = config[CONF_ID]
|
||||
light_path = CORE.config.get_path_for_id(light_id)[:-1]
|
||||
light_config = CORE.config.get_config_for_path(light_path)
|
||||
light_config = fv.get_declaring_config(light_id, CORE.config)
|
||||
effects = light_config.get(CONF_EFFECTS, [])
|
||||
index = find_effect_index(effects, original_name)
|
||||
if index is not None:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "light_color_values.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -229,13 +230,7 @@ class LightCall {
|
||||
inline bool get_save_() { return (this->flags_ & FLAG_SAVE) != 0; }
|
||||
|
||||
// Helper to set flag - defaults to true for common case
|
||||
void set_flag_(FieldFlags flag, bool value = true) ESPHOME_ALWAYS_INLINE {
|
||||
if (value) {
|
||||
this->flags_ |= flag;
|
||||
} else {
|
||||
this->flags_ &= ~flag;
|
||||
}
|
||||
}
|
||||
void set_flag_(FieldFlags flag, bool value = true) ESPHOME_ALWAYS_INLINE { assign_bits(this->flags_, flag, value); }
|
||||
|
||||
// Helper to clear flag - reduces code size for common case
|
||||
void clear_flag_(FieldFlags flag) ESPHOME_ALWAYS_INLINE { this->flags_ &= ~flag; }
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "logger.h"
|
||||
|
||||
#include "esphome/components/libretiny/core.h"
|
||||
#ifdef USE_LIBRETINY_CRASH_HANDLER
|
||||
#include "esphome/components/libretiny/crash_handler.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::logger {
|
||||
|
||||
@@ -52,6 +55,9 @@ void Logger::pre_setup() {
|
||||
|
||||
global_logger = this;
|
||||
ESP_LOGI(TAG, "Log initialized");
|
||||
#ifdef USE_LIBRETINY_CRASH_HANDLER
|
||||
libretiny::crash_handler_log();
|
||||
#endif
|
||||
}
|
||||
|
||||
const LogString *Logger::get_uart_selection_() {
|
||||
@@ -68,6 +74,11 @@ const LogString *Logger::get_uart_selection_() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_LIBRETINY_CRASH_HANDLER
|
||||
bool crash_log_pending() { return libretiny::crash_handler_has_data(); }
|
||||
void log_crash_to_client() { libretiny::crash_handler_log(); }
|
||||
#endif
|
||||
|
||||
} // namespace esphome::logger
|
||||
|
||||
#endif // USE_LIBRETINY
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "media_player.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
@@ -77,19 +78,12 @@ const char *media_player_command_to_string(MediaPlayerCommand command) {
|
||||
}
|
||||
|
||||
void MediaPlayerTraits::set_supports_pause(bool supports_pause) {
|
||||
if (supports_pause) {
|
||||
this->feature_flags_ |= MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY;
|
||||
} else {
|
||||
this->feature_flags_ &= ~(MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY);
|
||||
}
|
||||
assign_bits(this->feature_flags_, MediaPlayerEntityFeature::PAUSE | MediaPlayerEntityFeature::PLAY, supports_pause);
|
||||
}
|
||||
|
||||
void MediaPlayerTraits::set_supports_turn_off_on(bool supports_turn_off_on) {
|
||||
if (supports_turn_off_on) {
|
||||
this->feature_flags_ |= MediaPlayerEntityFeature::TURN_OFF | MediaPlayerEntityFeature::TURN_ON;
|
||||
} else {
|
||||
this->feature_flags_ &= ~(MediaPlayerEntityFeature::TURN_OFF | MediaPlayerEntityFeature::TURN_ON);
|
||||
}
|
||||
assign_bits(this->feature_flags_, MediaPlayerEntityFeature::TURN_OFF | MediaPlayerEntityFeature::TURN_ON,
|
||||
supports_turn_off_on);
|
||||
}
|
||||
|
||||
void MediaPlayerCall::validate_() {
|
||||
|
||||
@@ -41,9 +41,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
full_config = fv.full_config.get()
|
||||
display_path = full_config.get_path_for_id(config[CONF_DISPLAY_ID])[:-1]
|
||||
display_config = full_config.get_config_for_path(display_path)
|
||||
display_config = fv.get_declaring_config(config[CONF_DISPLAY_ID])
|
||||
if CONF_BRIGHTNESS not in display_config:
|
||||
model = display_config[CONF_MODEL]
|
||||
if model == "CUSTOM":
|
||||
|
||||
@@ -310,9 +310,7 @@ def migrate_custom_command(config: ConfigType) -> None:
|
||||
frame = config.get(CONF_CUSTOM_COMMAND)
|
||||
if frame is None:
|
||||
return
|
||||
fconf = fv.full_config.get()
|
||||
path = fconf.get_path_for_id(config[CONF_MODBUS_CONTROLLER_ID])[:-1]
|
||||
controller = fconf.get_config_for_path(path)
|
||||
controller = fv.get_declaring_config(config[CONF_MODBUS_CONTROLLER_ID])
|
||||
# the controller's DEVICE address (from modbus_device_schema)
|
||||
address = controller[CONF_ADDRESS]
|
||||
if frame[0] != address:
|
||||
@@ -342,9 +340,7 @@ def _reject_continuous_write_custom_pdu(config: ConfigType) -> None:
|
||||
pdu = config.get(CONF_CUSTOM_PDU)
|
||||
if pdu is None or not modbus.is_function_code_write(pdu[0]):
|
||||
return
|
||||
fconf = fv.full_config.get()
|
||||
path = fconf.get_path_for_id(config[CONF_MODBUS_CONTROLLER_ID])[:-1]
|
||||
controller = fconf.get_config_for_path(path)
|
||||
controller = fv.get_declaring_config(config[CONF_MODBUS_CONTROLLER_ID])
|
||||
if controller.get(CONF_CONTINUOUS) is True:
|
||||
raise cv.Invalid(
|
||||
f"a '{CONF_CUSTOM_PDU}' with a write function code (0x{pdu[0] & 0x7F:02X}) can't be polled "
|
||||
@@ -359,9 +355,7 @@ def _reject_broadcastable_custom_pdu(config: ConfigType) -> None:
|
||||
pdu = config.get(CONF_CUSTOM_PDU)
|
||||
if pdu is None or not modbus.is_function_code_broadcastable(pdu[0]):
|
||||
return
|
||||
fconf = fv.full_config.get()
|
||||
path = fconf.get_path_for_id(config[CONF_MODBUS_CONTROLLER_ID])[:-1]
|
||||
controller = fconf.get_config_for_path(path)
|
||||
controller = fv.get_declaring_config(config[CONF_MODBUS_CONTROLLER_ID])
|
||||
if (
|
||||
controller.get(CONF_ADDRESS) == modbus.BROADCAST_ADDRESS
|
||||
and controller.get(modbus.CONF_ALLOW_BROADCAST_READ) is True
|
||||
@@ -384,9 +378,7 @@ def validate_custom_pdu_item(config: ConfigType) -> None:
|
||||
def _reject_write_option_off_broadcast(config: ConfigType) -> None:
|
||||
if not any(config.get(key) is True for key in modbus.broadcast_only_option_keys()):
|
||||
return
|
||||
fconf = fv.full_config.get()
|
||||
path = fconf.get_path_for_id(config[CONF_MODBUS_CONTROLLER_ID])[:-1]
|
||||
controller = fconf.get_config_for_path(path)
|
||||
controller = fv.get_declaring_config(config[CONF_MODBUS_CONTROLLER_ID])
|
||||
if controller.get(CONF_ADDRESS) != modbus.BROADCAST_ADDRESS:
|
||||
raise cv.Invalid(
|
||||
f"'{modbus.CONF_EXPECT_BROADCAST_WRITE_RESPONSE}' only applies when the "
|
||||
|
||||
@@ -50,9 +50,7 @@ MAX_RECOMMENDED_UPDATE_INTERVAL_MS = 100
|
||||
|
||||
def get_motion_config(motion_id: ID) -> dict:
|
||||
"""Look up the (already-validated) config of the motion hub referenced by motion_id."""
|
||||
full_config = fv.full_config.get()
|
||||
motion_path = full_config.get_path_for_id(motion_id)[:-1]
|
||||
return full_config.get_config_for_path(motion_path)
|
||||
return fv.get_declaring_config(motion_id)
|
||||
|
||||
|
||||
def check_update_interval(motion_id: ID, feature_name: str) -> None:
|
||||
|
||||
@@ -67,8 +67,7 @@ def _final_validate(config: ConfigType) -> None:
|
||||
"Max touch channel must equal the highest binary sensor channel or be removed for auto calculation",
|
||||
path=[CONF_MAX_TOUCH_CHANNEL],
|
||||
)
|
||||
path = fconf.get_path_for_id(config[CONF_ID])[:-1]
|
||||
this_config = fconf.get_config_for_path(path)
|
||||
this_config = fv.get_declaring_config(config[CONF_ID], fconf)
|
||||
this_config[CONF_MAX_TOUCH_CHANNEL] = max_touch_channel
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::mpr121 {
|
||||
@@ -84,11 +85,7 @@ bool MPR121Component::digital_read(uint8_t pin) { return (this->gpio_input_ & (1
|
||||
|
||||
void MPR121Component::digital_write(uint8_t pin, bool value) {
|
||||
const uint8_t ionum = pin - 4;
|
||||
if (value) {
|
||||
this->gpio_output_ |= (1 << ionum);
|
||||
} else {
|
||||
this->gpio_output_ &= ~(1 << ionum);
|
||||
}
|
||||
assign_bits(this->gpio_output_, 1 << ionum, value);
|
||||
this->flush_gpio_();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,49 +32,22 @@ bool MPU6050Component::setup_registers_() {
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Power Management");
|
||||
// Setup power management
|
||||
uint8_t power_management;
|
||||
if (!this->read_byte(MPU6050_REGISTER_POWER_MANAGEMENT_1, &power_management)) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input power_management: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(power_management));
|
||||
// Set clock source - X-Gyro
|
||||
power_management &= 0b11111000;
|
||||
power_management |= MPU6050_CLOCK_SOURCE_X_GYRO;
|
||||
// Disable sleep
|
||||
power_management &= ~(1 << MPU6050_BIT_SLEEP_ENABLED);
|
||||
// Enable temperature
|
||||
power_management &= ~(1 << MPU6050_BIT_TEMPERATURE_DISABLED);
|
||||
ESP_LOGV(TAG, " Output power_management: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(power_management));
|
||||
if (!this->write_byte(MPU6050_REGISTER_POWER_MANAGEMENT_1, power_management)) {
|
||||
// Clock source X-Gyro, sleep off, temperature on
|
||||
if (!this->update_register(MPU6050_REGISTER_POWER_MANAGEMENT_1,
|
||||
0b111 | (1 << MPU6050_BIT_SLEEP_ENABLED) | (1 << MPU6050_BIT_TEMPERATURE_DISABLED),
|
||||
MPU6050_CLOCK_SOURCE_X_GYRO)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Gyro Config");
|
||||
// Set scale - 2000DPS
|
||||
uint8_t gyro_config;
|
||||
if (!this->read_byte(MPU6050_REGISTER_GYRO_CONFIG, &gyro_config)) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||
gyro_config &= 0b11100111;
|
||||
gyro_config |= MPU6050_SCALE_2000_DPS << 3;
|
||||
ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||
if (!this->write_byte(MPU6050_REGISTER_GYRO_CONFIG, gyro_config)) {
|
||||
if (!this->update_register(MPU6050_REGISTER_GYRO_CONFIG, 0b00011000, MPU6050_SCALE_2000_DPS << 3)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Accel Config");
|
||||
// Set range - 2G
|
||||
uint8_t accel_config;
|
||||
if (!this->read_byte(MPU6050_REGISTER_ACCEL_CONFIG, &accel_config)) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||
accel_config &= 0b11100111;
|
||||
accel_config |= (MPU6050_RANGE_2G << 3);
|
||||
ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||
return this->write_byte(MPU6050_REGISTER_ACCEL_CONFIG, accel_config);
|
||||
return this->update_register(MPU6050_REGISTER_ACCEL_CONFIG, 0b00011000, MPU6050_RANGE_2G << 3);
|
||||
}
|
||||
void MPU6050Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MPU6050:");
|
||||
|
||||
@@ -36,49 +36,22 @@ bool MPU6886Component::setup_registers_() {
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Power Management");
|
||||
// Setup power management
|
||||
uint8_t power_management;
|
||||
if (!this->read_byte(MPU6886_REGISTER_POWER_MANAGEMENT_1, &power_management)) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input power_management: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(power_management));
|
||||
// Set clock source - X-Gyro
|
||||
power_management &= 0b11111000;
|
||||
power_management |= MPU6886_CLOCK_SOURCE_X_GYRO;
|
||||
// Disable sleep
|
||||
power_management &= ~(1 << MPU6886_BIT_SLEEP_ENABLED);
|
||||
// Enable temperature
|
||||
power_management &= ~(1 << MPU6886_BIT_TEMPERATURE_DISABLED);
|
||||
ESP_LOGV(TAG, " Output power_management: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(power_management));
|
||||
if (!this->write_byte(MPU6886_REGISTER_POWER_MANAGEMENT_1, power_management)) {
|
||||
// Clock source X-Gyro, sleep off, temperature on
|
||||
if (!this->update_register(MPU6886_REGISTER_POWER_MANAGEMENT_1,
|
||||
0b111 | (1 << MPU6886_BIT_SLEEP_ENABLED) | (1 << MPU6886_BIT_TEMPERATURE_DISABLED),
|
||||
MPU6886_CLOCK_SOURCE_X_GYRO)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Gyroscope Config");
|
||||
// Set scale - 2000DPS
|
||||
uint8_t gyro_config;
|
||||
if (!this->read_byte(MPU6886_REGISTER_GYRO_CONFIG, &gyro_config)) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input gyroscope_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||
gyro_config &= 0b11100111;
|
||||
gyro_config |= MPU6886_SCALE_2000_DPS << 3;
|
||||
ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||
if (!this->write_byte(MPU6886_REGISTER_GYRO_CONFIG, gyro_config)) {
|
||||
if (!this->update_register(MPU6886_REGISTER_GYRO_CONFIG, 0b00011000, MPU6886_SCALE_2000_DPS << 3)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, " Setting up Accelerometer Config");
|
||||
// Set range - 2G
|
||||
uint8_t accel_config;
|
||||
if (!this->read_byte(MPU6886_REGISTER_ACCEL_CONFIG, &accel_config)) {
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, " Input accelerometer_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||
accel_config &= 0b11100111;
|
||||
accel_config |= (MPU6886_RANGE_2G << 3);
|
||||
ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||
return this->write_byte(MPU6886_REGISTER_ACCEL_CONFIG, accel_config);
|
||||
return this->update_register(MPU6886_REGISTER_ACCEL_CONFIG, 0b00011000, MPU6886_RANGE_2G << 3);
|
||||
}
|
||||
|
||||
void MPU6886Component::dump_config() {
|
||||
|
||||
@@ -168,11 +168,6 @@ ValueRangeTrigger = number_ns.class_(
|
||||
"ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component
|
||||
)
|
||||
|
||||
# Conditions
|
||||
NumberInRangeCondition = number_ns.class_(
|
||||
"NumberInRangeCondition", automation.Condition
|
||||
)
|
||||
|
||||
NumberMode = number_ns.enum("NumberMode")
|
||||
|
||||
# Schema default that also matches the C++ initializer in number_traits.h; codegen
|
||||
@@ -347,17 +342,12 @@ NUMBER_IN_RANGE_CONDITION_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"number.in_range", NumberInRangeCondition, NUMBER_IN_RANGE_CONDITION_SCHEMA
|
||||
automation.register_apply_condition(
|
||||
"number.in_range",
|
||||
NUMBER_IN_RANGE_CONDITION_SCHEMA,
|
||||
automation.IN_RANGE_CHECK,
|
||||
defaults=automation.IN_RANGE_DEFAULTS,
|
||||
)
|
||||
async def number_in_range_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(condition_id, template_arg, paren)
|
||||
|
||||
cg.add_optional(var.set_min, config.get(CONF_ABOVE))
|
||||
cg.add_optional(var.set_max, config.get(CONF_BELOW))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CORE)
|
||||
|
||||
@@ -26,27 +26,4 @@ class ValueRangeTrigger final : public Trigger<float>, public Component {
|
||||
TemplatableFn<float, float> max_{[](float) -> float { return NAN; }};
|
||||
};
|
||||
|
||||
template<typename... Ts> class NumberInRangeCondition final : public Condition<Ts...> {
|
||||
public:
|
||||
NumberInRangeCondition(Number *parent) : parent_(parent) {}
|
||||
|
||||
void set_min(float min) { this->min_ = min; }
|
||||
void set_max(float max) { this->max_ = max; }
|
||||
bool check(const Ts &...x) override {
|
||||
const float state = this->parent_->state;
|
||||
if (std::isnan(this->min_)) {
|
||||
return state <= this->max_;
|
||||
} else if (std::isnan(this->max_)) {
|
||||
return state >= this->min_;
|
||||
} else {
|
||||
return this->min_ <= state && state <= this->max_;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Number *parent_;
|
||||
float min_{NAN};
|
||||
float max_{NAN};
|
||||
};
|
||||
|
||||
} // namespace esphome::number
|
||||
@@ -48,9 +48,7 @@ def _final_validate(config: ConfigType) -> None:
|
||||
if config[CONF_TYPE] != CONF_STATUS:
|
||||
# Only run this validation if a status sensor is being configured
|
||||
return
|
||||
full_config = fv.full_config.get()
|
||||
transport_path = full_config.get_path_for_id(config[CONF_TRANSPORT_ID])[:-1]
|
||||
transport_config = full_config.get_config_for_path(transport_path)
|
||||
transport_config = fv.get_declaring_config(config[CONF_TRANSPORT_ID])
|
||||
if transport_config[CONF_PING_PONG_ENABLE] and any(
|
||||
CONF_ENCRYPTION in p
|
||||
for p in transport_config[CONF_PROVIDERS]
|
||||
|
||||
@@ -35,11 +35,8 @@ def validate_from_to(value: ConfigType) -> ConfigType:
|
||||
|
||||
|
||||
def validate_segment(config: ConfigType) -> None:
|
||||
fconf = fv.full_config.get()
|
||||
|
||||
if CONF_ID in config: # only validate addressable segments
|
||||
path = fconf.get_path_for_id(config[CONF_ID])[:-1]
|
||||
segment_light_config = fconf.get_config_for_path(path)
|
||||
segment_light_config = fv.get_declaring_config(config[CONF_ID])
|
||||
|
||||
if CONF_NUM_LEDS in segment_light_config:
|
||||
segment_len = segment_light_config[CONF_NUM_LEDS]
|
||||
|
||||
@@ -46,11 +46,7 @@ class ProvisioningManager : public Component {
|
||||
void set_source_provisioned(uint8_t source, bool provisioned) {
|
||||
if (source >= MAX_SOURCES)
|
||||
return;
|
||||
if (provisioned) {
|
||||
this->provisioned_mask_ |= (1UL << source);
|
||||
} else {
|
||||
this->provisioned_mask_ &= ~(1UL << source);
|
||||
}
|
||||
assign_bits(this->provisioned_mask_, 1UL << source, provisioned);
|
||||
}
|
||||
|
||||
// True once every registered source is provisioned. Config validation guarantees
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
from collections.abc import Callable
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
from string import ascii_letters, digits
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
@@ -35,6 +33,7 @@ from esphome.core import (
|
||||
from esphome.core.config import BOARD_MAX_LENGTH
|
||||
from esphome.helpers import copy_file_if_changed, read_file, write_file_if_changed
|
||||
from esphome.platformio.toolchain import copy_ccache_script
|
||||
from esphome.stacktrace import process_crash_handler_line as process_stacktrace # noqa: F401
|
||||
from esphome.storage_json import StorageJSON
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -656,48 +655,3 @@ def copy_files() -> None:
|
||||
path = CORE.relative_src_path("esphome.h")
|
||||
content = read_file(path).rstrip("\n")
|
||||
write_file_if_changed(path, content + '\n#include "pio_includes.h"\n')
|
||||
|
||||
|
||||
# RP2040 crash handler stacktrace decoding
|
||||
# Matches output from esphome/components/rp2/crash_handler.cpp
|
||||
_CRASH_RE = re.compile(r"CRASH DETECTED ON PREVIOUS BOOT")
|
||||
_CRASH_ADDR_RE = re.compile(
|
||||
r"(?:PC|LR|BT\d):\s+(0x[0-9a-fA-F]{8})\s+\((?:fault location|return address|stack backtrace)\)"
|
||||
)
|
||||
|
||||
|
||||
def _addr2line(tool: str, elf: Path, addr: str) -> str:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[tool, "-pfiaC", "-e", str(elf), addr],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
return f"{addr} (decode failed)"
|
||||
|
||||
|
||||
def process_stacktrace(config: ConfigType, line: str, backtrace_state: bool) -> bool:
|
||||
"""Decode RP2040 crash handler output using addr2line."""
|
||||
if _CRASH_RE.search(line):
|
||||
_LOGGER.error("RP2040 crash detected - decoding addresses")
|
||||
return True
|
||||
|
||||
if backtrace_state:
|
||||
if match := _CRASH_ADDR_RE.search(line):
|
||||
from esphome.platformio.toolchain import get_idedata
|
||||
|
||||
idedata = get_idedata(config)
|
||||
if idedata.addr2line_path:
|
||||
elf = idedata.firmware_elf_path
|
||||
if elf.exists():
|
||||
decoded = _addr2line(idedata.addr2line_path, elf, match.group(1))
|
||||
_LOGGER.error(" %s => %s", match.group(1), decoded)
|
||||
|
||||
# Stop backtrace state after addr2line hint (last line of crash dump)
|
||||
if "addr2line" in line:
|
||||
return False
|
||||
|
||||
return backtrace_state
|
||||
@@ -323,7 +323,6 @@ ToNTCResistanceFilter = sensor_ns.class_("ToNTCResistanceFilter", Filter)
|
||||
ToNTCTemperatureFilter = sensor_ns.class_("ToNTCTemperatureFilter", Filter)
|
||||
RHCorrectionFilter = sensor_ns.class_("RHCorrectionFilter", Filter)
|
||||
CalibratePolynomialFilter = sensor_ns.class_("CalibratePolynomialFilter", Filter)
|
||||
SensorInRangeCondition = sensor_ns.class_("SensorInRangeCondition", Filter)
|
||||
ClampFilter = sensor_ns.class_("ClampFilter", Filter)
|
||||
RoundFilter = sensor_ns.class_("RoundFilter", Filter)
|
||||
RoundMultipleFilter = sensor_ns.class_("RoundMultipleFilter", Filter)
|
||||
@@ -1124,17 +1123,12 @@ SENSOR_IN_RANGE_CONDITION_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"sensor.in_range", SensorInRangeCondition, SENSOR_IN_RANGE_CONDITION_SCHEMA
|
||||
automation.register_apply_condition(
|
||||
"sensor.in_range",
|
||||
SENSOR_IN_RANGE_CONDITION_SCHEMA,
|
||||
automation.IN_RANGE_CHECK,
|
||||
defaults=automation.IN_RANGE_DEFAULTS,
|
||||
)
|
||||
async def sensor_in_range_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(condition_id, template_arg, paren)
|
||||
|
||||
cg.add_optional(var.set_min, config.get(CONF_ABOVE))
|
||||
cg.add_optional(var.set_max, config.get(CONF_BELOW))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
def validate_ntc_calibration_parameter(value):
|
||||
|
||||
@@ -58,27 +58,4 @@ class ValueRangeTrigger final : public Trigger<float>, public Component {
|
||||
TemplatableFn<float, float> max_{[](float) -> float { return NAN; }};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SensorInRangeCondition final : public Condition<Ts...> {
|
||||
public:
|
||||
SensorInRangeCondition(Sensor *parent) : parent_(parent) {}
|
||||
|
||||
void set_min(float min) { this->min_ = min; }
|
||||
void set_max(float max) { this->max_ = max; }
|
||||
bool check(const Ts &...x) override {
|
||||
const float state = this->parent_->state;
|
||||
if (std::isnan(this->min_)) {
|
||||
return state <= this->max_;
|
||||
} else if (std::isnan(this->max_)) {
|
||||
return state >= this->min_;
|
||||
} else {
|
||||
return this->min_ <= state && state <= this->max_;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Sensor *parent_;
|
||||
float min_{NAN};
|
||||
float max_{NAN};
|
||||
};
|
||||
|
||||
} // namespace esphome::sensor
|
||||
@@ -62,11 +62,7 @@ int BSDSocketImpl::setblocking(bool blocking) {
|
||||
if (fl < 0) {
|
||||
return fl;
|
||||
}
|
||||
if (blocking) {
|
||||
fl &= ~O_NONBLOCK;
|
||||
} else {
|
||||
fl |= O_NONBLOCK;
|
||||
}
|
||||
assign_bits(fl, O_NONBLOCK, !blocking);
|
||||
return ::fcntl(this->fd_, F_SETFL, fl);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +52,7 @@ int LwIPSocketImpl::setblocking(bool blocking) {
|
||||
if (fl < 0) {
|
||||
return fl;
|
||||
}
|
||||
if (blocking) {
|
||||
fl &= ~O_NONBLOCK;
|
||||
} else {
|
||||
fl |= O_NONBLOCK;
|
||||
}
|
||||
assign_bits(fl, O_NONBLOCK, !blocking);
|
||||
return lwip_fcntl(this->fd_, F_SETFL, fl);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,24 @@ PlayAction = speaker_ns.class_(
|
||||
)
|
||||
|
||||
|
||||
def get_audio_dac_ids(config):
|
||||
"""Return the audio DAC ids of a speaker config: none, the single id, or the list."""
|
||||
audio_dac_ids = config.get(CONF_AUDIO_DAC)
|
||||
if audio_dac_ids is None:
|
||||
return []
|
||||
return audio_dac_ids if isinstance(audio_dac_ids, list) else [audio_dac_ids]
|
||||
|
||||
|
||||
async def setup_speaker_core_(var, config):
|
||||
await cg.add_variable(var.set_audio_dac, config.get(CONF_AUDIO_DAC))
|
||||
if audio_dacs := get_audio_dac_ids(config):
|
||||
dacs = [await cg.get_variable(dac_id) for dac_id in audio_dacs]
|
||||
table = cg.shared_progmem_array(
|
||||
"speaker_audio_dacs",
|
||||
audio_dac.AudioDac.operator("ptr"),
|
||||
cg.ArrayInitializer(*dacs),
|
||||
constexpr=False,
|
||||
)
|
||||
cg.add(var.set_audio_dacs(table, len(dacs)))
|
||||
|
||||
|
||||
async def register_speaker(var, config):
|
||||
@@ -31,9 +47,26 @@ async def register_speaker(var, config):
|
||||
await setup_speaker_core_(var, config)
|
||||
|
||||
|
||||
def _unique_audio_dacs(value):
|
||||
# a repeated DAC would get every volume, mute and audio start write twice
|
||||
if len(set(value)) != len(value):
|
||||
raise cv.Invalid("Each audio_dac can only be listed once")
|
||||
return value
|
||||
|
||||
|
||||
SPEAKER_SCHEMA = cv.Schema.extend(audio.AUDIO_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.Optional(CONF_AUDIO_DAC): cv.use_id(audio_dac.AudioDac),
|
||||
# one id as before (kept as a single value), or a list of DACs fed by this speaker's audio output that all
|
||||
# get the same volume and mute
|
||||
# the list comes first so a bad list reports its own error
|
||||
cv.Optional(CONF_AUDIO_DAC): cv.Any(
|
||||
cv.All(
|
||||
[cv.use_id(audio_dac.AudioDac)],
|
||||
cv.Length(min=1),
|
||||
_unique_audio_dacs,
|
||||
),
|
||||
cv.use_id(audio_dac.AudioDac),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -69,15 +69,15 @@ class Speaker {
|
||||
bool is_stopped() const { return this->state_ == STATE_STOPPED; }
|
||||
|
||||
// Volume and mute are independent: changing one never alters the other's stored state. Volume control is
|
||||
// handled by a configured audio dac component. Individual speaker components can override and implement in
|
||||
// software if an audio dac isn't available.
|
||||
// handled by the configured audio dac components, every one of them getting the same volume and mute. Individual
|
||||
// speaker components can override and implement in software if an audio dac isn't available.
|
||||
virtual void set_volume(float volume) {
|
||||
this->volume_ = volume;
|
||||
#ifdef USE_AUDIO_DAC
|
||||
if (this->audio_dac_ != nullptr) {
|
||||
this->audio_dac_->set_volume(volume);
|
||||
this->apply_audio_dac_mute_();
|
||||
for (auto *audio_dac : this->audio_dacs_) {
|
||||
audio_dac->set_volume(volume);
|
||||
}
|
||||
this->apply_audio_dac_mute_();
|
||||
#endif
|
||||
};
|
||||
virtual float get_volume() { return this->volume_; }
|
||||
@@ -91,7 +91,14 @@ class Speaker {
|
||||
virtual bool get_mute_state() { return this->mute_state_; }
|
||||
|
||||
#ifdef USE_AUDIO_DAC
|
||||
void set_audio_dac(audio_dac::AudioDac *audio_dac) { this->audio_dac_ = audio_dac; }
|
||||
/// Sets a single audio dac (nullptr for none), replacing any list; keeps its own copy of the pointer.
|
||||
void set_audio_dac(audio_dac::AudioDac *audio_dac) {
|
||||
this->audio_dacs_.assign_copy(&audio_dac, audio_dac != nullptr ? 1 : 0);
|
||||
}
|
||||
/// Codegen only: the table must outlive this speaker. Every DAC listed is fed by this speaker's audio output.
|
||||
void set_audio_dacs(audio_dac::AudioDac *const *audio_dacs, size_t count) {
|
||||
this->audio_dacs_.assign_static(audio_dacs, count);
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_audio_stream_info(const audio::AudioStreamInfo &audio_stream_info) {
|
||||
@@ -114,14 +121,25 @@ class Speaker {
|
||||
bool is_silent_() const { return this->mute_state_ || this->volume_ < SILENT_VOLUME_THRESHOLD; }
|
||||
|
||||
#ifdef USE_AUDIO_DAC
|
||||
/// @brief Uses the audio dac's mute as the silence mechanism, since a dac's minimum volume is often audible.
|
||||
/// @brief Whether volume and mute are handled in hardware by at least one audio dac.
|
||||
bool has_audio_dac_() const { return !this->audio_dacs_.empty(); }
|
||||
|
||||
/// @brief Uses the audio dacs' mute as the silence mechanism, since a dac's minimum volume is often audible.
|
||||
void apply_audio_dac_mute_() {
|
||||
if (this->audio_dac_ == nullptr)
|
||||
return;
|
||||
if (this->is_silent_()) {
|
||||
this->audio_dac_->set_mute_on();
|
||||
} else {
|
||||
this->audio_dac_->set_mute_off();
|
||||
const bool silent = this->is_silent_();
|
||||
for (auto *audio_dac : this->audio_dacs_) {
|
||||
if (silent) {
|
||||
audio_dac->set_mute_on();
|
||||
} else {
|
||||
audio_dac->set_mute_off();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Tells every audio dac that its audio clocks are running; call from the main loop.
|
||||
void notify_audio_dacs_started_() {
|
||||
for (auto *audio_dac : this->audio_dacs_) {
|
||||
audio_dac->on_audio_started();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -132,7 +150,8 @@ class Speaker {
|
||||
float volume_{1.0f};
|
||||
|
||||
#ifdef USE_AUDIO_DAC
|
||||
audio_dac::AudioDac *audio_dac_{nullptr};
|
||||
// all audio dacs fed by this speaker's audio output (eg two amplifiers on one I2S bus); empty without an audio dac
|
||||
ConstVector<audio_dac::AudioDac *, true> audio_dacs_;
|
||||
#endif
|
||||
|
||||
CallbackManager<void(uint32_t, int64_t)> audio_output_callback_{};
|
||||
|
||||
@@ -153,7 +153,6 @@ void SX1509Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
|
||||
void SX1509Component::setup_led_driver(uint8_t pin) {
|
||||
uint16_t temp_word = 0;
|
||||
uint8_t temp_byte = 0;
|
||||
|
||||
this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word);
|
||||
temp_word |= (1 << pin);
|
||||
@@ -162,16 +161,11 @@ void SX1509Component::setup_led_driver(uint8_t pin) {
|
||||
this->ddr_mask_ &= ~(1 << pin); // 0=output
|
||||
this->write_byte_16(REG_DIR_B, this->ddr_mask_);
|
||||
|
||||
this->read_byte(REG_CLOCK, &temp_byte);
|
||||
temp_byte |= (1 << 6); // Internal 2MHz oscillator part 1 (set bit 6)
|
||||
temp_byte &= ~(1 << 5); // Internal 2MHz oscillator part 2 (clear bit 5)
|
||||
this->write_byte(REG_CLOCK, temp_byte);
|
||||
// Internal 2MHz oscillator: set bit 6, clear bit 5
|
||||
this->update_register(REG_CLOCK, (1 << 6) | (1 << 5), 1 << 6);
|
||||
|
||||
this->read_byte(REG_MISC, &temp_byte);
|
||||
temp_byte &= ~(1 << 7); // set linear mode bank B
|
||||
temp_byte &= ~(1 << 3); // set linear mode bank A
|
||||
temp_byte |= 0x70; // Frequency of the LED Driver clock ClkX of all IOs:
|
||||
this->write_byte(REG_MISC, temp_byte);
|
||||
// Linear mode for banks A (bit 3) and B (bit 7); LED driver clock ClkX of all IOs (bits 6:4)
|
||||
this->update_register(REG_MISC, (1 << 7) | (1 << 3) | 0x70, 0x70);
|
||||
|
||||
this->read_byte_16(REG_LED_DRIVER_ENABLE_B, &temp_word);
|
||||
temp_word |= (1 << pin);
|
||||
@@ -194,11 +188,7 @@ void SX1509Component::clock_(uint8_t osc_source, uint8_t osc_pin_function, uint8
|
||||
this->clk_x_ = 2000000;
|
||||
osc_divider = (osc_divider & 0b111) << 4; // 3-bit value, bits 6:4
|
||||
|
||||
uint8_t reg_misc = 0;
|
||||
this->read_byte(REG_MISC, ®_misc);
|
||||
reg_misc &= ~(0b111 << 4);
|
||||
reg_misc |= osc_divider;
|
||||
this->write_byte(REG_MISC, reg_misc);
|
||||
this->update_register(REG_MISC, 0b111 << 4, osc_divider);
|
||||
}
|
||||
|
||||
void SX1509Component::setup_keypad_() {
|
||||
@@ -212,15 +202,11 @@ void SX1509Component::setup_keypad_() {
|
||||
this->ddr_mask_ |= (1 << i);
|
||||
this->write_byte_16(REG_DIR_B, this->ddr_mask_);
|
||||
|
||||
this->read_byte(REG_OPEN_DRAIN_A, &temp_byte);
|
||||
for (int i = 0; i < this->rows_; i++)
|
||||
temp_byte |= (1 << i);
|
||||
this->write_byte(REG_OPEN_DRAIN_A, temp_byte);
|
||||
const uint8_t row_bits = (1 << this->rows_) - 1;
|
||||
this->update_register(REG_OPEN_DRAIN_A, row_bits, row_bits);
|
||||
|
||||
this->read_byte(REG_PULL_UP_B, &temp_byte);
|
||||
for (int i = 0; i < this->cols_; i++)
|
||||
temp_byte |= (1 << i);
|
||||
this->write_byte(REG_PULL_UP_B, temp_byte);
|
||||
const uint8_t col_bits = (1 << this->cols_) - 1;
|
||||
this->update_register(REG_PULL_UP_B, col_bits, col_bits);
|
||||
|
||||
if (debounce_time_ >= scan_time_) {
|
||||
debounce_time_ = scan_time_ >> 1; // Force debounce_time to be less than scan_time
|
||||
@@ -249,13 +235,8 @@ uint16_t SX1509Component::read_key_data() {
|
||||
|
||||
void SX1509Component::set_debounce_config_(uint8_t config_value) {
|
||||
// First make sure clock is configured
|
||||
uint8_t temp_byte = 0;
|
||||
this->read_byte(REG_MISC, &temp_byte);
|
||||
temp_byte |= (1 << 4); // Just default to no divider if not set
|
||||
this->write_byte(REG_MISC, temp_byte);
|
||||
this->read_byte(REG_CLOCK, &temp_byte);
|
||||
temp_byte |= (1 << 6); // default to internal osc.
|
||||
this->write_byte(REG_CLOCK, temp_byte);
|
||||
this->update_register(REG_MISC, 1 << 4, 1 << 4); // Just default to no divider if not set
|
||||
this->update_register(REG_CLOCK, 1 << 6, 1 << 6); // default to internal osc.
|
||||
|
||||
config_value &= 0b111; // 3-bit value
|
||||
this->write_byte(REG_DEBOUNCE_CONFIG, config_value);
|
||||
|
||||
@@ -45,13 +45,10 @@ bool SY6970Component::write_register_(uint8_t reg, uint8_t value) {
|
||||
}
|
||||
|
||||
bool SY6970Component::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
|
||||
uint8_t reg_value;
|
||||
if (!this->read_byte(reg, ®_value)) {
|
||||
ESP_LOGW(TAG, "Failed to read register 0x%02X for update", reg);
|
||||
return false;
|
||||
}
|
||||
reg_value = (reg_value & ~mask) | (value & mask);
|
||||
return this->write_register_(reg, reg_value);
|
||||
if (this->update_register(reg, mask, value))
|
||||
return true;
|
||||
ESP_LOGW(TAG, "Failed to update register 0x%02X", reg);
|
||||
return false;
|
||||
}
|
||||
|
||||
void SY6970Component::setup() {
|
||||
|
||||
@@ -159,12 +159,10 @@ bool TAS2780::select_page_(uint8_t page) {
|
||||
}
|
||||
|
||||
bool TAS2780::update_bits_(uint8_t reg, uint8_t mask, uint8_t value) {
|
||||
uint8_t current;
|
||||
if (!this->select_page_(TAS2780_PAGE_0) || !this->read_byte(reg, ¤t)) {
|
||||
ESP_LOGE(TAG, "Failed to read register 0x%02X", reg);
|
||||
return false;
|
||||
}
|
||||
return this->write_byte(reg, (current & ~mask) | (value & mask));
|
||||
if (this->select_page_(TAS2780_PAGE_0) && this->update_register(reg, mask, value))
|
||||
return true;
|
||||
ESP_LOGW(TAG, "Failed to update register 0x%02X", reg);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TAS2780::init_() {
|
||||
|
||||
@@ -56,9 +56,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
fconf = fv.full_config.get()
|
||||
hub_path = fconf.get_path_for_id(config[CONF_TAS58XX_ID])
|
||||
hub_conf = fconf.get_config_for_path(hub_path[:-1])
|
||||
hub_conf = fv.get_declaring_config(config[CONF_TAS58XX_ID])
|
||||
|
||||
if hub_conf[CONF_MODEL] == DAC_TAS5825M:
|
||||
return config
|
||||
|
||||
@@ -65,9 +65,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
"""Resolve the key to the number the device reports, checked against the keypad's matrix."""
|
||||
full_config = fv.full_config.get()
|
||||
keypad_path = full_config.get_path_for_id(config[CONF_KEYPAD_ID])[:-1]
|
||||
keypad = full_config.get_config_for_path(keypad_path)
|
||||
keypad = fv.get_declaring_config(config[CONF_KEYPAD_ID])
|
||||
rows = keypad.get(CONF_ROWS, 0)
|
||||
columns = keypad.get(CONF_COLUMNS, 0)
|
||||
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
from esphome.const import CONF_ADDRESS, CONF_FACTORY_RESET, CONF_RESTART
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -23,18 +14,8 @@ RestartButton = tfluna_ns.class_("RestartButton", button.Button)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_TFLUNA_ID): cv.use_id(TFLunaComponent),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
ResetButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
RestartButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.factory_reset_button_schema(ResetButton),
|
||||
cv.Optional(CONF_RESTART): button.restart_button_schema(RestartButton),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -59,9 +59,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
if CONF_ACTUAL_CURRENT not in config:
|
||||
return config
|
||||
full_config = fv.full_config.get()
|
||||
path = full_config.get_path_for_id(config[CONF_TMC2209_ID])[:-1]
|
||||
if full_config.get_config_for_path(path).get(CONF_ANALOG_CURRENT_SCALE):
|
||||
if fv.get_declaring_config(config[CONF_TMC2209_ID]).get(CONF_ANALOG_CURRENT_SCALE):
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_ACTUAL_CURRENT}' can't be used with '{CONF_ANALOG_CURRENT_SCALE}', "
|
||||
"the current then depends on VREF",
|
||||
|
||||
@@ -56,8 +56,7 @@ _validate_unique = unique_child("number")
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
_validate_unique(config)
|
||||
full = fv.full_config.get()
|
||||
parent = full.get_config_for_path(full.get_path_for_id(config[CONF_TMP102_ID])[:-1])
|
||||
parent = fv.get_declaring_config(config[CONF_TMP102_ID])
|
||||
add_number_parent(config[CONF_TMP102_ID], parent.get(CONF_EXTENDED_MODE, False))
|
||||
return config
|
||||
|
||||
|
||||
@@ -162,11 +162,7 @@ void HostUartComponent::setup() {
|
||||
break;
|
||||
};
|
||||
// Set stop bits
|
||||
if (this->stop_bits_ == 2) {
|
||||
options.c_cflag |= CSTOPB;
|
||||
} else {
|
||||
options.c_cflag &= ~CSTOPB;
|
||||
}
|
||||
assign_bits(options.c_cflag, CSTOPB, this->stop_bits_ == 2);
|
||||
cfsetispeed(&options, baud);
|
||||
cfsetospeed(&options, baud);
|
||||
tcsetattr(this->file_descriptor_, TCSANOW, &options);
|
||||
|
||||
@@ -17,14 +17,8 @@ void UFireECComponent::setup() {
|
||||
ESP_LOGI(TAG, "Found ufire_ec board version 0x%02X", version);
|
||||
|
||||
// Write option for temperature adjustments
|
||||
uint8_t config;
|
||||
this->read_byte(REGISTER_CONFIG, &config);
|
||||
if (this->temperature_sensor_ == nullptr && this->temperature_sensor_external_ == nullptr) {
|
||||
config &= ~CONFIG_TEMP_COMPENSATION;
|
||||
} else {
|
||||
config |= CONFIG_TEMP_COMPENSATION;
|
||||
}
|
||||
this->write_byte(REGISTER_CONFIG, config);
|
||||
const bool compensate = this->temperature_sensor_ != nullptr || this->temperature_sensor_external_ != nullptr;
|
||||
this->update_register(REGISTER_CONFIG, CONFIG_TEMP_COMPENSATION, compensate ? CONFIG_TEMP_COMPENSATION : 0);
|
||||
|
||||
// Update temperature compensation
|
||||
this->set_compensation_(this->temperature_compensation_);
|
||||
|
||||
@@ -18,14 +18,8 @@ void UFireISEComponent::setup() {
|
||||
ESP_LOGI(TAG, "Found uFire_ise board version 0x%02X", version);
|
||||
|
||||
// Write option for temperature adjustments
|
||||
uint8_t config;
|
||||
this->read_byte(REGISTER_CONFIG, &config);
|
||||
if (this->temperature_sensor_ == nullptr && this->temperature_sensor_external_ == nullptr) {
|
||||
config &= ~CONFIG_TEMP_COMPENSATION;
|
||||
} else {
|
||||
config |= CONFIG_TEMP_COMPENSATION;
|
||||
}
|
||||
this->write_byte(REGISTER_CONFIG, config);
|
||||
const bool compensate = this->temperature_sensor_ != nullptr || this->temperature_sensor_external_ != nullptr;
|
||||
this->update_register(REGISTER_CONFIG, CONFIG_TEMP_COMPENSATION, compensate ? CONFIG_TEMP_COMPENSATION : 0);
|
||||
}
|
||||
|
||||
void UFireISEComponent::update() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -59,21 +60,13 @@ WaterHeaterCall &WaterHeaterCall::set_target_temperature_high(float temperature)
|
||||
}
|
||||
|
||||
WaterHeaterCall &WaterHeaterCall::set_away(bool away) {
|
||||
if (away) {
|
||||
this->state_ |= WATER_HEATER_STATE_AWAY;
|
||||
} else {
|
||||
this->state_ &= ~WATER_HEATER_STATE_AWAY;
|
||||
}
|
||||
assign_bits(this->state_, WATER_HEATER_STATE_AWAY, away);
|
||||
this->state_mask_ |= WATER_HEATER_STATE_AWAY;
|
||||
return *this;
|
||||
}
|
||||
|
||||
WaterHeaterCall &WaterHeaterCall::set_on(bool on) {
|
||||
if (on) {
|
||||
this->state_ |= WATER_HEATER_STATE_ON;
|
||||
} else {
|
||||
this->state_ &= ~WATER_HEATER_STATE_ON;
|
||||
}
|
||||
assign_bits(this->state_, WATER_HEATER_STATE_ON, on);
|
||||
this->state_mask_ |= WATER_HEATER_STATE_ON;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -252,13 +252,7 @@ class WaterHeater : public EntityBase {
|
||||
/// Set the state flags. Should only be called from control().
|
||||
void set_state_(uint32_t state) { this->state_ = state; }
|
||||
/// Set or clear a state flag. Should only be called from control().
|
||||
void set_state_flag_(uint32_t flag, bool value) {
|
||||
if (value) {
|
||||
this->state_ |= flag;
|
||||
} else {
|
||||
this->state_ &= ~flag;
|
||||
}
|
||||
}
|
||||
void set_state_flag_(uint32_t flag, bool value) { assign_bits(this->state_, flag, value); }
|
||||
|
||||
WaterHeaterMode mode_{WATER_HEATER_MODE_OFF};
|
||||
float current_temperature_{NAN};
|
||||
|
||||
@@ -16,6 +16,7 @@ from esphome.types import ConfigType
|
||||
DEPENDENCIES = ["wifi"]
|
||||
|
||||
wifi_info_ns = cg.esphome_ns.namespace("wifi_info")
|
||||
_request_ip_sensor_slot = cg.slot_counter("WIFI_INFO_IP_SENSOR_COUNT")
|
||||
IPAddressWiFiInfo = wifi_info_ns.class_(
|
||||
"IPAddressWiFiInfo", text_sensor.TextSensor, cg.Component
|
||||
)
|
||||
@@ -110,7 +111,10 @@ async def to_code(config: ConfigType) -> None:
|
||||
if conf := config.get(CONF_IP_ADDRESS):
|
||||
wifi_info = await text_sensor.new_text_sensor(config[CONF_IP_ADDRESS])
|
||||
await cg.register_component(wifi_info, config[CONF_IP_ADDRESS])
|
||||
for x in range(5):
|
||||
if sensor_conf := conf.get(f"address_{x}"):
|
||||
sens = await text_sensor.new_text_sensor(sensor_conf)
|
||||
cg.add(wifi_info.add_ip_sensors(x, sens))
|
||||
indexes = [x for x in range(5) if f"address_{x}" in conf]
|
||||
# One shared array size: the largest highest index of any entry, so add_ip_sensors(x) stays in range
|
||||
for _ in range(max(indexes, default=-1) + 1):
|
||||
_request_ip_sensor_slot(str(wifi_info))
|
||||
for x in indexes:
|
||||
sens = await text_sensor.new_text_sensor(conf[f"address_{x}"])
|
||||
cg.add(wifi_info.add_ip_sensors(x, sens))
|
||||
@@ -26,16 +26,20 @@ void IPAddressWiFiInfo::on_ip_state(const network::IPAddresses &ips, const netwo
|
||||
char buf[network::IP_ADDRESS_BUFFER_SIZE];
|
||||
ips[0].str_to(buf);
|
||||
this->publish_state(buf);
|
||||
#ifdef WIFI_INFO_IP_SENSOR_COUNT
|
||||
uint8_t sensor = 0;
|
||||
for (const auto &ip : ips) {
|
||||
if (ip.is_set()) {
|
||||
if (this->ip_sensors_[sensor] != nullptr) {
|
||||
ip.str_to(buf);
|
||||
this->ip_sensors_[sensor]->publish_state(buf);
|
||||
}
|
||||
sensor++;
|
||||
if (!ip.is_set())
|
||||
continue;
|
||||
if (sensor == WIFI_INFO_IP_SENSOR_COUNT)
|
||||
break;
|
||||
if (this->ip_sensors_[sensor] != nullptr) {
|
||||
ip.str_to(buf);
|
||||
this->ip_sensors_[sensor]->publish_state(buf);
|
||||
}
|
||||
sensor++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*********************
|
||||
|
||||
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user