1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 08:41:59 +00:00

Merge remote-tracking branch 'upstream/dev' into integration

This commit is contained in:
J. Nick Koston
2026-01-04 17:03:54 -10:00
47 changed files with 1048 additions and 134 deletions

View File

@@ -575,5 +575,6 @@ esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow
esphome/components/zephyr/* @tomaszduda23
esphome/components/zhlt01/* @cfeenstra1024
esphome/components/zigbee/* @tomaszduda23
esphome/components/zio_ultrasonic/* @kahrendt
esphome/components/zwave_proxy/* @kbx81

View File

@@ -3,7 +3,7 @@ from logging import getLogger
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
from esphome.components import mqtt, web_server, zigbee
from esphome.components.const import CONF_ON_STATE_CHANGE
import esphome.config_validation as cv
from esphome.const import (
@@ -439,6 +439,7 @@ def validate_publish_initial_state(value):
_BINARY_SENSOR_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
.extend(cv.MQTT_COMPONENT_SCHEMA)
.extend(zigbee.BINARY_SENSOR_SCHEMA)
.extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
@@ -520,6 +521,7 @@ _BINARY_SENSOR_SCHEMA = (
_BINARY_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("binary_sensor"))
_BINARY_SENSOR_SCHEMA.add_extra(zigbee.validate_binary_sensor)
def binary_sensor_schema(
@@ -621,6 +623,8 @@ async def setup_binary_sensor_core_(var, config):
if web_server_config := config.get(CONF_WEB_SERVER):
await web_server.add_entity_config(var, web_server_config)
await zigbee.setup_binary_sensor(var, config)
async def register_binary_sensor(var, config):
if not CORE.has_id(config[CONF_ID]):

View File

@@ -138,20 +138,21 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
}
void PIDAutotuner::dump_config() {
if (this->state_ == AUTOTUNE_SUCCEEDED) {
ESP_LOGI(TAG, "%s: PID Autotune:", this->id_.c_str());
ESP_LOGI(TAG, " State: Succeeded!");
ESP_LOGI(TAG,
"%s: PID Autotune:\n"
" State: Succeeded!",
this->id_.c_str());
bool has_issue = false;
if (!this->amplitude_detector_.is_amplitude_convergent()) {
ESP_LOGW(TAG, " Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!");
ESP_LOGW(TAG, " Please make sure you eliminate all outside influences on the measured temperature.");
ESP_LOGW(TAG, " Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!\n"
" Please make sure you eliminate all outside influences on the measured temperature.");
has_issue = true;
}
if (!this->frequency_detector_.is_increase_decrease_symmetrical()) {
ESP_LOGW(TAG, " Oscillation Frequency is not symmetrical. PID parameters may be inaccurate!");
ESP_LOGW(
TAG,
" This is usually because the heat and cool processes do not change the temperature at the same rate.");
ESP_LOGW(TAG,
" Oscillation Frequency is not symmetrical. PID parameters may be inaccurate!\n"
" This is usually because the heat and cool processes do not change the temperature at the same "
"rate.\n"
" Please try reducing the positive_output value (or increase negative_output in case of a cooler)");
has_issue = true;
}
@@ -160,18 +161,23 @@ void PIDAutotuner::dump_config() {
}
auto fac = get_ziegler_nichols_pid_();
ESP_LOGI(TAG, " Calculated PID parameters (\"Ziegler-Nichols PID\" rule):");
ESP_LOGI(TAG, " ");
ESP_LOGI(TAG, " control_parameters:");
ESP_LOGI(TAG, " kp: %.5f", fac.kp);
ESP_LOGI(TAG, " ki: %.5f", fac.ki);
ESP_LOGI(TAG, " kd: %.5f", fac.kd);
ESP_LOGI(TAG, " ");
ESP_LOGI(TAG, " Please copy these values into your YAML configuration! They will reset on the next reboot.");
ESP_LOGI(TAG,
" Calculated PID parameters (\"Ziegler-Nichols PID\" rule):\n"
"\n"
" control_parameters:\n"
" kp: %.5f\n"
" ki: %.5f\n"
" kd: %.5f\n"
"\n"
" Please copy these values into your YAML configuration! They will reset on the next reboot.",
fac.kp, fac.ki, fac.kd);
ESP_LOGV(TAG, " Oscillation Period: %f", this->frequency_detector_.get_mean_oscillation_period());
ESP_LOGV(TAG, " Oscillation Amplitude: %f", this->amplitude_detector_.get_mean_oscillation_amplitude());
ESP_LOGV(TAG, " Ku: %f, Pu: %f", this->ku_, this->pu_);
ESP_LOGV(TAG,
" Oscillation Period: %f\n"
" Oscillation Amplitude: %f\n"
" Ku: %f, Pu: %f",
this->frequency_detector_.get_mean_oscillation_period(),
this->amplitude_detector_.get_mean_oscillation_amplitude(), this->ku_, this->pu_);
ESP_LOGD(TAG, " Alternative Rules:");
// http://www.mstarlabs.com/control/znrule.html
@@ -183,13 +189,16 @@ void PIDAutotuner::dump_config() {
}
if (this->state_ == AUTOTUNE_RUNNING) {
ESP_LOGD(TAG, "%s: PID Autotune:", this->id_.c_str());
ESP_LOGD(TAG, " Autotune is still running!");
ESP_LOGD(TAG, " Status: Trying to reach %.2f °C", setpoint_ - relay_function_.current_target_error());
ESP_LOGD(TAG, " Stats so far:");
ESP_LOGD(TAG, " Phases: %" PRIu32, relay_function_.phase_count);
ESP_LOGD(TAG, " Detected %zu zero-crossings", frequency_detector_.zerocrossing_intervals.size());
ESP_LOGD(TAG, " Current Phase Min: %.2f, Max: %.2f", amplitude_detector_.phase_min,
ESP_LOGD(TAG,
"%s: PID Autotune:\n"
" Autotune is still running!\n"
" Status: Trying to reach %.2f °C\n"
" Stats so far:\n"
" Phases: %" PRIu32 "\n"
" Detected %zu zero-crossings\n"
" Current Phase Min: %.2f, Max: %.2f",
this->id_.c_str(), setpoint_ - relay_function_.current_target_error(), relay_function_.phase_count,
frequency_detector_.zerocrossing_intervals.size(), amplitude_detector_.phase_min,
amplitude_detector_.phase_max);
}
}
@@ -205,8 +214,10 @@ PIDAutotuner::PIDResult PIDAutotuner::calculate_pid_(float kp_factor, float ki_f
}
void PIDAutotuner::print_rule_(const char *name, float kp_factor, float ki_factor, float kd_factor) {
auto fac = calculate_pid_(kp_factor, ki_factor, kd_factor);
ESP_LOGD(TAG, " Rule '%s':", name);
ESP_LOGD(TAG, " kp: %.5f, ki: %.5f, kd: %.5f", fac.kp, fac.ki, fac.kd);
ESP_LOGD(TAG,
" Rule '%s':\n"
" kp: %.5f, ki: %.5f, kd: %.5f",
name, fac.kp, fac.ki, fac.kd);
}
// ================== RelayFunction ==================

View File

@@ -68,8 +68,10 @@ bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) {
next_pcnt_channel = pcnt_channel_t(int(next_pcnt_channel) + 1);
}
ESP_LOGCONFIG(TAG, " PCNT Unit Number: %u", this->pcnt_unit);
ESP_LOGCONFIG(TAG, " PCNT Channel Number: %u", this->pcnt_channel);
ESP_LOGCONFIG(TAG,
" PCNT Unit Number: %u\n"
" PCNT Channel Number: %u",
this->pcnt_unit, this->pcnt_channel);
pcnt_count_mode_t rising = PCNT_COUNT_DIS, falling = PCNT_COUNT_DIS;
switch (this->rising_edge_mode) {

View File

@@ -127,9 +127,11 @@ bool QMP6988Component::get_calibration_data_() {
qmp6988_data_.qmp6988_cali.COE_b21 = (int16_t) encode_uint16(a_data_uint8_tr[14], a_data_uint8_tr[15]);
qmp6988_data_.qmp6988_cali.COE_bp3 = (int16_t) encode_uint16(a_data_uint8_tr[16], a_data_uint8_tr[17]);
ESP_LOGV(TAG, "<-----------calibration data-------------->\r\n");
ESP_LOGV(TAG, "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_a0,
qmp6988_data_.qmp6988_cali.COE_a1, qmp6988_data_.qmp6988_cali.COE_a2, qmp6988_data_.qmp6988_cali.COE_b00);
ESP_LOGV(TAG,
"<-----------calibration data-------------->\n"
"COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]",
qmp6988_data_.qmp6988_cali.COE_a0, qmp6988_data_.qmp6988_cali.COE_a1, qmp6988_data_.qmp6988_cali.COE_a2,
qmp6988_data_.qmp6988_cali.COE_b00);
ESP_LOGV(TAG, "COE_bt1[%d] COE_bt2[%d] COE_bp1[%d] COE_b11[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_bt1,
qmp6988_data_.qmp6988_cali.COE_bt2, qmp6988_data_.qmp6988_cali.COE_bp1, qmp6988_data_.qmp6988_cali.COE_b11);
ESP_LOGV(TAG, "COE_bp2[%d] COE_b12[%d] COE_b21[%d] COE_bp3[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_bp2,
@@ -150,9 +152,10 @@ bool QMP6988Component::get_calibration_data_() {
qmp6988_data_.ik.b12 = 6846L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b12 + 85590281L; // 29Q53
qmp6988_data_.ik.b21 = 13836L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b21 + 79333336L; // 29Q60
qmp6988_data_.ik.bp3 = 2915L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp3 + 157155561L; // 28Q65
ESP_LOGV(TAG, "<----------- int calibration data -------------->\r\n");
ESP_LOGV(TAG, "a0[%d] a1[%d] a2[%d] b00[%d]\r\n", qmp6988_data_.ik.a0, qmp6988_data_.ik.a1, qmp6988_data_.ik.a2,
qmp6988_data_.ik.b00);
ESP_LOGV(TAG,
"<----------- int calibration data -------------->\n"
"a0[%d] a1[%d] a2[%d] b00[%d]",
qmp6988_data_.ik.a0, qmp6988_data_.ik.a1, qmp6988_data_.ik.a2, qmp6988_data_.ik.b00);
ESP_LOGV(TAG, "bt1[%lld] bt2[%lld] bp1[%lld] b11[%lld]\r\n", qmp6988_data_.ik.bt1, qmp6988_data_.ik.bt2,
qmp6988_data_.ik.bp1, qmp6988_data_.ik.b11);
ESP_LOGV(TAG, "bp2[%lld] b12[%lld] b21[%lld] bp3[%lld]\r\n", qmp6988_data_.ik.bp2, qmp6988_data_.ik.b12,
@@ -330,8 +333,10 @@ void QMP6988Component::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " Temperature Oversampling: %s", oversampling_to_str(this->temperature_oversampling_));
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
ESP_LOGCONFIG(TAG, " Pressure Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
ESP_LOGCONFIG(TAG,
" Pressure Oversampling: %s\n"
" IIR Filter: %s",
oversampling_to_str(this->pressure_oversampling_), iir_filter_to_str(this->iir_filter_));
}
void QMP6988Component::update() {

View File

@@ -118,10 +118,11 @@ void RadonEyeRD200::read_sensors_(uint8_t *value, uint16_t value_len) {
radon_long_term_sensor_->publish_state(radon_day);
}
ESP_LOGV(TAG, " Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f", radon_now, radon_day, radon_month);
ESP_LOGV(TAG, " Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f", radon_now / convert_to_bwpm3,
radon_day / convert_to_bwpm3, radon_month / convert_to_bwpm3);
ESP_LOGV(TAG,
" Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f\n"
" Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f",
radon_now, radon_day, radon_month, radon_now / convert_to_bwpm3, radon_day / convert_to_bwpm3,
radon_month / convert_to_bwpm3);
// This instance must not stay connected
// so other clients can connect to it (e.g. the

View File

@@ -104,8 +104,10 @@ void ProntoProtocol::send_pronto_(RemoteTransmitData *dst, const std::vector<uin
uint16_t intros = 2 * data[2];
uint16_t repeats = 2 * data[3];
ESP_LOGD(TAG, "Send Pronto: intros=%d", intros);
ESP_LOGD(TAG, "Send Pronto: repeats=%d", repeats);
ESP_LOGD(TAG,
"Send Pronto: intros=%d\n"
"Send Pronto: repeats=%d",
intros, repeats);
if (NUMBERS_IN_PREAMBLE + intros + repeats != data.size()) { // inconsistent sizes
ESP_LOGE(TAG, "Inconsistent data, not sending");
return;

View File

@@ -76,9 +76,8 @@ void RemoteReceiverComponent::setup() {
}
void RemoteReceiverComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Remote Receiver:");
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG,
"Remote Receiver:\n"
" Buffer Size: %u\n"
" Tolerance: %u%s\n"
" Filter out pulses shorter than: %u us\n"
@@ -86,6 +85,7 @@ void RemoteReceiverComponent::dump_config() {
this->buffer_size_, this->tolerance_,
(this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_,
this->idle_us_);
LOG_PIN(" Pin: ", this->pin_);
}
void RemoteReceiverComponent::loop() {

View File

@@ -117,9 +117,8 @@ void RemoteReceiverComponent::setup() {
}
void RemoteReceiverComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Remote Receiver:");
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG,
"Remote Receiver:\n"
" Clock resolution: %" PRIu32 " hz\n"
" RMT symbols: %" PRIu32 "\n"
" Filter symbols: %" PRIu32 "\n"
@@ -132,6 +131,7 @@ void RemoteReceiverComponent::dump_config() {
this->clock_resolution_, this->rmt_symbols_, this->filter_symbols_, this->receive_symbols_,
this->tolerance_, (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%",
this->carrier_frequency_, this->carrier_duty_percent_, this->filter_us_, this->idle_us_);
LOG_PIN(" Pin: ", this->pin_);
if (this->is_failed()) {
ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_),
this->error_string_.c_str());

View File

@@ -36,9 +36,11 @@ void RP2040PWM::setup_pwm_() {
}
void RP2040PWM::dump_config() {
ESP_LOGCONFIG(TAG, "RP2040 PWM:");
ESP_LOGCONFIG(TAG,
"RP2040 PWM:\n"
" Frequency: %.1f Hz",
this->frequency_);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_);
LOG_FLOAT_OUTPUT(this);
}
void HOT RP2040PWM::write_state(float state) {

View File

@@ -27,8 +27,10 @@ void RuntimeStatsCollector::record_component_time(Component *component, uint32_t
}
void RuntimeStatsCollector::log_stats_() {
ESP_LOGI(TAG, "Component Runtime Statistics");
ESP_LOGI(TAG, "Period stats (last %" PRIu32 "ms):", this->log_interval_);
ESP_LOGI(TAG,
"Component Runtime Statistics\n"
"Period stats (last %" PRIu32 "ms):",
this->log_interval_);
// First collect stats we want to display
std::vector<ComponentStatPair> stats_to_display;

View File

@@ -40,8 +40,10 @@ void SafeModeComponent::dump_config() {
#ifdef USE_OTA_ROLLBACK
const esp_partition_t *last_invalid = esp_ota_get_last_invalid_partition();
if (last_invalid != nullptr) {
ESP_LOGW(TAG, "OTA rollback detected! Rolled back from partition '%s'", last_invalid->label);
ESP_LOGW(TAG, "The device reset before the boot was marked successful");
ESP_LOGW(TAG,
"OTA rollback detected! Rolled back from partition '%s'\n"
"The device reset before the boot was marked successful",
last_invalid->label);
}
#endif
}

View File

@@ -113,26 +113,20 @@ void ShellyDimmer::setup() {
void ShellyDimmer::update() { this->send_command_(SHELLY_DIMMER_PROTO_CMD_POLL, nullptr, 0); }
void ShellyDimmer::dump_config() {
ESP_LOGCONFIG(TAG, "ShellyDimmer:");
LOG_PIN(" NRST Pin: ", this->pin_nrst_);
LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_);
ESP_LOGCONFIG(TAG,
"ShellyDimmer:\n"
" Leading Edge: %s\n"
" Warmup Brightness: %d\n"
" Minimum Brightness: %d\n"
" Maximum Brightness: %d",
YESNO(this->leading_edge_), this->warmup_brightness_, this->min_brightness_, this->max_brightness_);
// ESP_LOGCONFIG(TAG, " Warmup Time: %d", this->warmup_time_);
// ESP_LOGCONFIG(TAG, " Fade Rate: %d", this->fade_rate_);
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG,
" STM32 current firmware version: %d.%d \n"
" Maximum Brightness: %d\n"
" STM32 current firmware version: %d.%d\n"
" STM32 required firmware version: %d.%d",
YESNO(this->leading_edge_), this->warmup_brightness_, this->min_brightness_, this->max_brightness_,
this->version_major_, this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION,
USE_SHD_FIRMWARE_MINOR_VERSION);
LOG_PIN(" NRST Pin: ", this->pin_nrst_);
LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_);
LOG_UPDATE_INTERVAL(this);
if (this->version_major_ != USE_SHD_FIRMWARE_MAJOR_VERSION ||
this->version_minor_ != USE_SHD_FIRMWARE_MINOR_VERSION) {
@@ -439,13 +433,15 @@ bool ShellyDimmer::handle_frame_() {
current = CURRENT_SCALING_FACTOR / static_cast<float>(current_raw);
}
ESP_LOGI(TAG, "Got dimmer data:");
ESP_LOGI(TAG, " HW version: %d", hw_version);
ESP_LOGI(TAG, " Brightness: %d", brightness);
ESP_LOGI(TAG, " Fade rate: %d", fade_rate);
ESP_LOGI(TAG, " Power: %f W", power);
ESP_LOGI(TAG, " Voltage: %f V", voltage);
ESP_LOGI(TAG, " Current: %f A", current);
ESP_LOGI(TAG,
"Got dimmer data:\n"
" HW version: %d\n"
" Brightness: %d\n"
" Fade rate: %d\n"
" Power: %f W\n"
" Voltage: %f V\n"
" Current: %f A",
hw_version, brightness, fade_rate, power, voltage, current);
// Update sensors.
if (this->power_sensor_ != nullptr) {

View File

@@ -60,8 +60,10 @@ void SHT3XDComponent::dump_config() {
ESP_LOGE(TAG, " Communication with SHT3xD failed!");
return;
}
ESP_LOGD(TAG, " Serial Number: 0x%08" PRIX32, this->serial_number_);
ESP_LOGD(TAG, " Heater Enabled: %s", this->heater_enabled_ ? "true" : "false");
ESP_LOGD(TAG,
" Serial Number: 0x%08" PRIX32 "\n"
" Heater Enabled: %s",
this->serial_number_, TRUEFALSE(this->heater_enabled_));
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);

View File

@@ -49,8 +49,10 @@ void SHTCXComponent::setup() {
}
void SHTCXComponent::dump_config() {
ESP_LOGCONFIG(TAG, "SHTCx:");
ESP_LOGCONFIG(TAG, " Model: %s (%04x)", to_string(this->type_), this->sensor_id_);
ESP_LOGCONFIG(TAG,
"SHTCx:\n"
" Model: %s (%04x)",
to_string(this->type_), this->sensor_id_);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);

View File

@@ -323,8 +323,10 @@ void Sim800LComponent::parse_cmd_(std::string message) {
kick ESPHome callback now
*/
if (ok || message.compare(0, 6, "+CMGL:") == 0) {
ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str());
ESP_LOGD(TAG, "%s", this->message_.c_str());
ESP_LOGD(TAG,
"Received SMS from: %s\n"
"%s",
this->sender_.c_str(), this->message_.c_str());
this->sms_received_callback_.call(this->message_, this->sender_);
this->state_ = STATE_RECEIVED_SMS;
} else {

View File

@@ -14,11 +14,13 @@ void SM16716::setup() {
this->pwm_amounts_.resize(this->num_channels_, 0);
}
void SM16716::dump_config() {
ESP_LOGCONFIG(TAG, "SM16716:");
ESP_LOGCONFIG(TAG,
"SM16716:\n"
" Total number of channels: %u\n"
" Number of chips: %u",
this->num_channels_, this->num_chips_);
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG, " Total number of channels: %u", this->num_channels_);
ESP_LOGCONFIG(TAG, " Number of chips: %u", this->num_chips_);
}
void SM16716::loop() {
if (!this->update_)

View File

@@ -34,11 +34,13 @@ void SM2135::setup() {
}
void SM2135::dump_config() {
ESP_LOGCONFIG(TAG, "SM2135:");
ESP_LOGCONFIG(TAG,
"SM2135:\n"
" CW Current: %dmA\n"
" RGB Current: %dmA",
10 + (this->cw_current_ * 5), 10 + (this->rgb_current_ * 5));
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG, " CW Current: %dmA", 10 + (this->cw_current_ * 5));
ESP_LOGCONFIG(TAG, " RGB Current: %dmA", 10 + (this->rgb_current_ * 5));
}
void SM2135::write_byte_(uint8_t data) {

View File

@@ -15,13 +15,13 @@ void SM2235::setup() {
}
void SM2235::dump_config() {
ESP_LOGCONFIG(TAG, "sm2235:");
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG,
"SM2235:\n"
" Color Channels Max Power: %u\n"
" White Channels Max Power: %u",
this->max_power_color_channels_, this->max_power_white_channels_);
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
}
} // namespace sm2235

View File

@@ -15,13 +15,13 @@ void SM2335::setup() {
}
void SM2335::dump_config() {
ESP_LOGCONFIG(TAG, "sm2335:");
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG,
"sm2335:\n"
" Color Channels Max Power: %u\n"
" White Channels Max Power: %u",
this->max_power_color_channels_, this->max_power_white_channels_);
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
}
} // namespace sm2335

View File

@@ -93,8 +93,10 @@ bool SonoffD1Output::read_command_(uint8_t *cmd, size_t &len) {
if (this->read_array(cmd, 6)) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char hex_buf[format_hex_pretty_size(6)];
ESP_LOGV(TAG, "[%04d] Reading from dimmer:", this->write_count_);
ESP_LOGV(TAG, "[%04d] %s", this->write_count_, format_hex_pretty_to(hex_buf, cmd, 6));
ESP_LOGV(TAG,
"[%04d] Reading from dimmer:\n"
"[%04d] %s",
this->write_count_, this->write_count_, format_hex_pretty_to(hex_buf, cmd, 6));
#endif
if (cmd[0] != 0xAA || cmd[1] != 0x55) {
@@ -188,8 +190,10 @@ bool SonoffD1Output::write_command_(uint8_t *cmd, const size_t len, bool needs_a
do {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char hex_buf[format_hex_pretty_size(SONOFF_D1_MAX_CMD_SIZE)];
ESP_LOGV(TAG, "[%04d] Writing to the dimmer:", this->write_count_);
ESP_LOGV(TAG, "[%04d] %s", this->write_count_, format_hex_pretty_to(hex_buf, cmd, len));
ESP_LOGV(TAG,
"[%04d] Writing to the dimmer:\n"
"[%04d] %s",
this->write_count_, this->write_count_, format_hex_pretty_to(hex_buf, cmd, len));
#endif
this->write_array(cmd, len);
this->write_count_++;

View File

@@ -11,9 +11,11 @@ static const char *const TAG = "spi_device";
void SPIDeviceComponent::setup() { this->spi_setup(); }
void SPIDeviceComponent::dump_config() {
ESP_LOGCONFIG(TAG, "SPIDevice");
ESP_LOGCONFIG(TAG,
"SPIDevice\n"
" Mode: %d",
this->mode_);
LOG_PIN(" CS pin: ", this->cs_);
ESP_LOGCONFIG(TAG, " Mode: %d", this->mode_);
if (this->data_rate_ < 1000000) {
ESP_LOGCONFIG(TAG, " Data rate: %" PRId32 "kHz", this->data_rate_ / 1000);
} else {

View File

@@ -34,8 +34,10 @@ light::LightTraits SpiLedStrip::get_traits() {
return traits;
}
void SpiLedStrip::dump_config() {
esph_log_config(TAG, "SPI LED Strip:");
esph_log_config(TAG, " LEDs: %d", this->num_leds_);
esph_log_config(TAG,
"SPI LED Strip:\n"
" LEDs: %d",
this->num_leds_);
if (this->data_rate_ >= spi::DATA_RATE_1MHZ) {
esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000));
} else {

View File

@@ -20,18 +20,18 @@ void I2CSSD1306::setup() {
}
void I2CSSD1306::dump_config() {
LOG_DISPLAY("", "I2C SSD1306", this);
LOG_I2C_DEVICE(this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" External VCC: %s\n"
" Flip X: %s\n"
" Flip Y: %s\n"
" Offset X: %d\n"
" Offset Y: %d\n"
" Inverted Color: %s",
YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_,
this->offset_y_, YESNO(this->invert_));
this->model_str_(), YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_),
this->offset_x_, this->offset_y_, YESNO(this->invert_));
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_UPDATE_INTERVAL(this);
if (this->error_code_ == COMMUNICATION_FAILED) {

View File

@@ -16,19 +16,19 @@ void SPISSD1306::setup() {
}
void SPISSD1306::dump_config() {
LOG_DISPLAY("", "SPI SSD1306", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" External VCC: %s\n"
" Flip X: %s\n"
" Flip Y: %s\n"
" Offset X: %d\n"
" Offset Y: %d\n"
" Inverted Color: %s",
YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_,
this->offset_y_, YESNO(this->invert_));
this->model_str_(), YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_),
this->offset_x_, this->offset_y_, YESNO(this->invert_));
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_UPDATE_INTERVAL(this);
}
void SPISSD1306::command(uint8_t value) {

View File

@@ -19,11 +19,13 @@ void SPISSD1322::setup() {
}
void SPISSD1322::dump_config() {
LOG_DISPLAY("", "SPI SSD1322", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" Initial Brightness: %.2f",
this->model_str_(), this->brightness_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_);
LOG_UPDATE_INTERVAL(this);
}
void SPISSD1322::command(uint8_t value) {

View File

@@ -19,12 +19,14 @@ void SPISSD1325::setup() {
}
void SPISSD1325::dump_config() {
LOG_DISPLAY("", "SPI SSD1325", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" Initial Brightness: %.2f\n"
" External VCC: %s",
this->model_str_(), this->brightness_, YESNO(this->external_vcc_));
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_);
ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_));
LOG_UPDATE_INTERVAL(this);
}
void SPISSD1325::command(uint8_t value) {

View File

@@ -19,11 +19,13 @@ void SPISSD1327::setup() {
}
void SPISSD1327::dump_config() {
LOG_DISPLAY("", "SPI SSD1327", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" Initial Brightness: %.2f",
this->model_str_(), this->brightness_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_);
LOG_UPDATE_INTERVAL(this);
}
void SPISSD1327::command(uint8_t value) {

View File

@@ -19,11 +19,13 @@ void SPISSD1351::setup() {
}
void SPISSD1351::dump_config() {
LOG_DISPLAY("", "SPI SSD1351", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" Initial Brightness: %.2f",
this->model_str_(), this->brightness_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_);
LOG_UPDATE_INTERVAL(this);
}
void SPISSD1351::command(uint8_t value) {

View File

@@ -20,14 +20,14 @@ void I2CST7567::setup() {
void I2CST7567::dump_config() {
LOG_DISPLAY("", "I2CST7567", this);
LOG_I2C_DEVICE(this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" Mirror X: %s\n"
" Mirror Y: %s\n"
" Invert Colors: %s",
YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_));
this->model_str_(), YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_));
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_UPDATE_INTERVAL(this);
if (this->error_code_ == COMMUNICATION_FAILED) {

View File

@@ -18,13 +18,15 @@ void SPIST7567::setup() {
void SPIST7567::dump_config() {
LOG_DISPLAY("", "SPI ST7567", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_());
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" Mirror X: %s\n"
" Mirror Y: %s\n"
" Invert Colors: %s",
this->model_str_(), YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_));
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->mirror_x_));
ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->mirror_y_));
ESP_LOGCONFIG(TAG, " Invert Colors: %s", YESNO(this->invert_colors_));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -127,15 +127,15 @@ void ST7789V::dump_config() {
" Width: %u\n"
" Height Offset: %u\n"
" Width Offset: %u\n"
" 8-bit color mode: %s",
" 8-bit color mode: %s\n"
" Data rate: %dMHz",
this->model_str_, this->height_, this->width_, this->offset_height_, this->offset_width_,
YESNO(this->eightbitcolor_));
YESNO(this->eightbitcolor_), (unsigned) (this->data_rate_ / 1000000));
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" B/L Pin: ", this->backlight_pin_);
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
#ifdef USE_POWER_SUPPLY
ESP_LOGCONFIG(TAG, " Power Supply Configured: yes");
#endif

View File

@@ -26,7 +26,12 @@ void arch_init() {
if (device_is_ready(WDT)) {
static wdt_timeout_cfg wdt_config{};
wdt_config.flags = WDT_FLAG_RESET_SOC;
#ifdef USE_ZIGBEE
// zboss thread use a lot of cpu cycles during start
wdt_config.window.max = 10000;
#else
wdt_config.window.max = 2000;
#endif
wdt_channel_id = wdt_install_timeout(WDT, &wdt_config);
if (wdt_channel_id >= 0) {
uint8_t options = 0;

View File

@@ -0,0 +1,124 @@
from typing import Any
from esphome import automation, core
import esphome.codegen as cg
from esphome.components.nrf52.boards import BOOTLOADER_CONFIG, Section
from esphome.components.zephyr import zephyr_add_pm_static, zephyr_data
from esphome.components.zephyr.const import KEY_BOOTLOADER
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INTERNAL
from esphome.core import CORE
from esphome.types import ConfigType
from .const_zephyr import (
CONF_MAX_EP_NUMBER,
CONF_ON_JOIN,
CONF_WIPE_ON_BOOT,
CONF_ZIGBEE_ID,
KEY_EP_NUMBER,
KEY_ZIGBEE,
ZigbeeComponent,
zigbee_ns,
)
from .zigbee_zephyr import zephyr_binary_sensor
CODEOWNERS = ["@tomaszduda23"]
def zigbee_set_core_data(config: ConfigType) -> ConfigType:
if zephyr_data()[KEY_BOOTLOADER] in BOOTLOADER_CONFIG:
zephyr_add_pm_static(
[Section("empty_after_zboss_offset", 0xF4000, 0xC000, "flash_primary")]
)
return config
BINARY_SENSOR_SCHEMA = cv.Schema({}).extend(zephyr_binary_sensor)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_ID): cv.declare_id(ZigbeeComponent),
cv.Optional(CONF_ON_JOIN): automation.validate_automation(single=True),
cv.Optional(CONF_WIPE_ON_BOOT, default=False): cv.All(
cv.boolean,
cv.requires_component("nrf52"),
),
}
).extend(cv.COMPONENT_SCHEMA),
zigbee_set_core_data,
cv.only_with_framework("zephyr"),
)
def validate_number_of_ep(config: ConfigType) -> None:
if KEY_ZIGBEE not in CORE.data:
raise cv.Invalid("At least one zigbee device need to be included")
count = len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])
if count == 1:
raise cv.Invalid(
"Single endpoint is not supported https://github.com/Koenkk/zigbee2mqtt/issues/29888"
)
if count > CONF_MAX_EP_NUMBER:
raise cv.Invalid(f"Maximum number of end points is {CONF_MAX_EP_NUMBER}")
FINAL_VALIDATE_SCHEMA = cv.All(
validate_number_of_ep,
)
async def to_code(config: ConfigType) -> None:
cg.add_define("USE_ZIGBEE")
if CORE.using_zephyr:
from .zigbee_zephyr import zephyr_to_code
await zephyr_to_code(config)
async def setup_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
return
if CORE.using_zephyr:
from .zigbee_zephyr import zephyr_setup_binary_sensor
await zephyr_setup_binary_sensor(entity, config)
def validate_binary_sensor(config: ConfigType) -> ConfigType:
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
return config
data: dict[str, Any] = CORE.data.setdefault(KEY_ZIGBEE, {})
slots: list[str] = data.setdefault(KEY_EP_NUMBER, [])
slots.extend([""])
return config
ZIGBEE_ACTION_SCHEMA = automation.maybe_simple_id(
cv.Schema(
{
cv.GenerateID(): cv.use_id(ZigbeeComponent),
}
)
)
FactoryResetAction = zigbee_ns.class_(
"FactoryResetAction", automation.Action, cg.Parented.template(ZigbeeComponent)
)
@automation.register_action(
"zigbee.factory_reset",
FactoryResetAction,
ZIGBEE_ACTION_SCHEMA,
)
async def reset_zigbee_to_code(
config: ConfigType,
action_id: core.ID,
template_arg: cg.TemplateArguments,
args: list[tuple],
) -> cg.Pvariable:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var

View File

@@ -0,0 +1,16 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ZIGBEE
#ifdef USE_NRF52
#include "zigbee_zephyr.h"
#endif
namespace esphome::zigbee {
template<typename... Ts> class FactoryResetAction : public Action<Ts...>, public Parented<ZigbeeComponent> {
public:
void play(const Ts &...x) override { this->parent_->factory_reset(); }
};
} // namespace esphome::zigbee
#endif

View File

@@ -0,0 +1,24 @@
import esphome.codegen as cg
zigbee_ns = cg.esphome_ns.namespace("zigbee")
ZigbeeComponent = zigbee_ns.class_("ZigbeeComponent", cg.Component)
BinaryAttrs = zigbee_ns.struct("BinaryAttrs")
CONF_MAX_EP_NUMBER = 8
CONF_ZIGBEE_ID = "zigbee_id"
CONF_ON_JOIN = "on_join"
CONF_WIPE_ON_BOOT = "wipe_on_boot"
CONF_ZIGBEE_BINARY_SENSOR = "zigbee_binary_sensor"
# Keys for CORE.data storage
KEY_ZIGBEE = "zigbee"
KEY_EP_NUMBER = "ep_number"
# External ZBOSS SDK types (just strings for codegen)
ZB_ZCL_BASIC_ATTRS_EXT_T = "zb_zcl_basic_attrs_ext_t"
ZB_ZCL_IDENTIFY_ATTRS_T = "zb_zcl_identify_attrs_t"
# Cluster IDs
ZB_ZCL_CLUSTER_ID_BASIC = "ZB_ZCL_CLUSTER_ID_BASIC"
ZB_ZCL_CLUSTER_ID_IDENTIFY = "ZB_ZCL_CLUSTER_ID_IDENTIFY"
ZB_ZCL_CLUSTER_ID_BINARY_INPUT = "ZB_ZCL_CLUSTER_ID_BINARY_INPUT"

View File

@@ -0,0 +1,37 @@
#include "zigbee_binary_sensor_zephyr.h"
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_BINARY_SENSOR)
#include "esphome/core/log.h"
extern "C" {
#include <zboss_api.h>
#include <zboss_api_addons.h>
#include <zb_nrf_platform.h>
#include <zigbee/zigbee_app_utils.h>
#include <zb_error_to_string.h>
}
namespace esphome::zigbee {
static const char *const TAG = "zigbee.binary_sensor";
ZigbeeBinarySensor::ZigbeeBinarySensor(binary_sensor::BinarySensor *binary_sensor) : binary_sensor_(binary_sensor) {}
void ZigbeeBinarySensor::setup() {
this->binary_sensor_->add_on_state_callback([this](bool state) {
this->cluster_attributes_->present_value = state ? ZB_TRUE : ZB_FALSE;
ESP_LOGD(TAG, "Set attribute end point: %d, present_value %d", this->end_point_,
this->cluster_attributes_->present_value);
ZB_ZCL_SET_ATTRIBUTE(this->end_point_, ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &this->cluster_attributes_->present_value,
ZB_FALSE);
this->parent_->flush();
});
}
void ZigbeeBinarySensor::dump_config() {
ESP_LOGCONFIG(TAG,
"Zigbee Binary Sensor\n"
" End point: %d, present_value %u",
this->end_point_, this->cluster_attributes_->present_value);
}
} // namespace esphome::zigbee
#endif

View File

@@ -0,0 +1,45 @@
#pragma once
#include "esphome/core/defines.h"
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_BINARY_SENSOR)
#include "esphome/components/zigbee/zigbee_zephyr.h"
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
extern "C" {
#include <zboss_api.h>
#include <zboss_api_addons.h>
}
// it should have been defined inside of sdk. It is missing though
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID(data_ptr) \
{ \
ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, ZB_ZCL_ATTR_TYPE_CHAR_STRING, ZB_ZCL_ATTR_ACCESS_READ_ONLY, \
(ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \
}
// copy of ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST + description
#define ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST(attr_list, out_of_service, present_value, status_flag, \
description) \
ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, ZB_ZCL_BINARY_INPUT) \
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_OUT_OF_SERVICE_ID, (out_of_service)) \
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, (present_value)) \
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_STATUS_FLAG_ID, (status_flag)) \
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (description)) \
ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST
namespace esphome::zigbee {
class ZigbeeBinarySensor : public ZigbeeEntity, public Component {
public:
explicit ZigbeeBinarySensor(binary_sensor::BinarySensor *binary_sensor);
void set_cluster_attributes(BinaryAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; }
void setup() override;
void dump_config() override;
protected:
BinaryAttrs *cluster_attributes_{nullptr};
binary_sensor::BinarySensor *binary_sensor_;
};
} // namespace esphome::zigbee
#endif

View File

@@ -0,0 +1,190 @@
#include "zigbee_zephyr.h"
#if defined(USE_ZIGBEE) && defined(USE_NRF52)
#include "esphome/core/log.h"
#include <zephyr/settings/settings.h>
#include <zephyr/storage/flash_map.h>
extern "C" {
#include <zboss_api.h>
#include <zboss_api_addons.h>
#include <zb_nrf_platform.h>
#include <zigbee/zigbee_app_utils.h>
#include <zb_error_to_string.h>
}
namespace esphome::zigbee {
static const char *const TAG = "zigbee";
ZigbeeComponent *global_zigbee = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
const uint8_t IEEE_ADDR_BUF_SIZE = 17;
void ZigbeeComponent::zboss_signal_handler_esphome(zb_bufid_t bufid) {
zb_zdo_app_signal_hdr_t *sig_hndler = nullptr;
zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, &sig_hndler);
zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid);
switch (sig) {
case ZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_SKIP_STARTUP, status: %d", status);
break;
case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY, status: %d", status);
break;
case ZB_ZDO_SIGNAL_LEAVE:
ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_LEAVE, status: %d", status);
break;
case ZB_BDB_SIGNAL_DEVICE_REBOOT:
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_REBOOT, status: %d", status);
if (status == RET_OK) {
on_join_();
}
break;
case ZB_BDB_SIGNAL_STEERING:
break;
case ZB_COMMON_SIGNAL_CAN_SLEEP:
ESP_LOGV(TAG, "ZB_COMMON_SIGNAL_CAN_SLEEP, status: %d", status);
break;
case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_FIRST_START, status: %d", status);
break;
case ZB_NLME_STATUS_INDICATION:
ESP_LOGD(TAG, "ZB_NLME_STATUS_INDICATION, status: %d", status);
break;
case ZB_BDB_SIGNAL_TC_REJOIN_DONE:
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_TC_REJOIN_DONE, status: %d", status);
break;
default:
ESP_LOGD(TAG, "zboss_signal_handler sig: %d, status: %d", sig, status);
break;
}
auto err = zigbee_default_signal_handler(bufid);
if (err != RET_OK) {
ESP_LOGE(TAG, "Zigbee_default_signal_handler ERROR %u [%s]", err, zb_error_to_string_get(err));
}
switch (sig) {
case ZB_BDB_SIGNAL_STEERING:
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_STEERING, status: %d", status);
if (status == RET_OK) {
zb_ext_pan_id_t extended_pan_id;
char ieee_addr_buf[IEEE_ADDR_BUF_SIZE] = {0};
int addr_len;
zb_get_extended_pan_id(extended_pan_id);
addr_len = ieee_addr_to_str(ieee_addr_buf, sizeof(ieee_addr_buf), extended_pan_id);
for (int i = 0; i < addr_len; ++i) {
if (ieee_addr_buf[i] != '0') {
on_join_();
break;
}
}
}
break;
}
/* All callbacks should either reuse or free passed buffers.
* If bufid == 0, the buffer is invalid (not passed).
*/
if (bufid) {
zb_buf_free(bufid);
}
}
void ZigbeeComponent::zcl_device_cb(zb_bufid_t bufid) {
zb_zcl_device_callback_param_t *p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
zb_zcl_device_callback_id_t device_cb_id = p_device_cb_param->device_cb_id;
zb_uint16_t cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
zb_uint16_t attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
auto endpoint = p_device_cb_param->endpoint;
ESP_LOGI(TAG, "Zcl_device_cb %s id %hd, cluster_id %d, attr_id %d, endpoint: %d", __func__, device_cb_id, cluster_id,
attr_id, endpoint);
// endpoints are enumerated from 1
if (global_zigbee->callbacks_.size() >= endpoint) {
global_zigbee->callbacks_[endpoint - 1](bufid);
return;
}
p_device_cb_param->status = RET_ERROR;
}
void ZigbeeComponent::on_join_() {
this->defer([this]() {
ESP_LOGD(TAG, "Joined the network");
this->join_trigger_.trigger();
this->join_cb_.call();
});
}
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
void ZigbeeComponent::erase_flash_(int area) {
const struct flash_area *fap;
flash_area_open(area, &fap);
flash_area_erase(fap, 0, fap->fa_size);
flash_area_close(fap);
}
#endif
void ZigbeeComponent::setup() {
global_zigbee = this;
auto err = settings_subsys_init();
if (err) {
ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err);
return;
}
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
erase_flash_(FIXED_PARTITION_ID(ZBOSS_NVRAM));
erase_flash_(FIXED_PARTITION_ID(ZBOSS_PRODUCT_CONFIG));
erase_flash_(FIXED_PARTITION_ID(SETTINGS_STORAGE));
#endif
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
err = settings_load();
if (err) {
ESP_LOGE(TAG, "Cannot load settings, err: %d", err);
return;
}
zigbee_enable();
}
void ZigbeeComponent::dump_config() {
bool wipe = false;
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
wipe = true;
#endif
ESP_LOGCONFIG(TAG,
"Zigbee\n"
" Wipe on boot: %s",
YESNO(wipe));
}
static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) {
ESP_LOGD(TAG, "Force zboss scheduler to wake and send attribute report");
zb_buf_free(bufid);
}
void ZigbeeComponent::flush() { this->need_flush_ = true; }
void ZigbeeComponent::loop() {
if (this->need_flush_) {
this->need_flush_ = false;
zb_buf_get_out_delayed_ext(send_attribute_report, 0, 0);
}
}
void ZigbeeComponent::factory_reset() {
ESP_LOGD(TAG, "Factory reset");
ZB_SCHEDULE_APP_CALLBACK(zb_bdb_reset_via_local_action, 0);
}
} // namespace esphome::zigbee
extern "C" void zboss_signal_handler(zb_uint8_t param) {
esphome::zigbee::global_zigbee->zboss_signal_handler_esphome(param);
}
#endif

View File

@@ -0,0 +1,104 @@
#pragma once
#include "esphome/core/defines.h"
#if defined(USE_ZIGBEE) && defined(USE_NRF52)
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
extern "C" {
#include <zboss_api.h>
#include <zboss_api_addons.h>
}
// copy of ZB_DECLARE_SIMPLE_DESC. Due to https://github.com/nrfconnect/sdk-nrfxlib/pull/666
#define ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clusters_count, out_clusters_count) \
typedef ZB_PACKED_PRE struct zb_af_simple_desc_##ep_name##_##in_clusters_count##_##out_clusters_count##_s { \
zb_uint8_t endpoint; /* Endpoint */ \
zb_uint16_t app_profile_id; /* Application profile identifier */ \
zb_uint16_t app_device_id; /* Application device identifier */ \
zb_bitfield_t app_device_version : 4; /* Application device version */ \
zb_bitfield_t reserved : 4; /* Reserved */ \
zb_uint8_t app_input_cluster_count; /* Application input cluster count */ \
zb_uint8_t app_output_cluster_count; /* Application output cluster count */ \
/* Application input and output cluster list */ \
zb_uint16_t app_cluster_list[(in_clusters_count) + (out_clusters_count)]; \
} ZB_PACKED_STRUCT zb_af_simple_desc_##ep_name##_##in_clusters_count##_##out_clusters_count##_t
#define ESPHOME_CAT7(a, b, c, d, e, f, g) a##b##c##d##e##f##g
// needed to use ESPHOME_ZB_DECLARE_SIMPLE_DESC
#define ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_num, out_num) \
ESPHOME_CAT7(zb_af_simple_desc_, ep_name, _, in_num, _, out_num, _t)
// needed to use ESPHOME_ZB_DECLARE_SIMPLE_DESC
#define ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num, ...) \
ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clust_num, out_clust_num); \
ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_clust_num, out_clust_num) \
simple_desc_##ep_name = {ep_id, ZB_AF_HA_PROFILE_ID, ZB_HA_SIMPLE_SENSOR_DEVICE_ID, 0, 0, in_clust_num, \
out_clust_num, {__VA_ARGS__}}
// needed to use ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC
#define ESPHOME_ZB_HA_DECLARE_EP(ep_name, ep_id, cluster_list, in_cluster_num, out_cluster_num, report_attr_count, \
...) \
ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_cluster_num, out_cluster_num, __VA_ARGS__); \
ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info##ep_name, report_attr_count); \
ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, \
ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \
(zb_af_simple_desc_1_1_t *) &simple_desc_##ep_name, report_attr_count, \
reporting_info##ep_name, 0, NULL)
namespace esphome::zigbee {
struct BinaryAttrs {
zb_bool_t out_of_service;
zb_bool_t present_value;
zb_uint8_t status_flags;
zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE];
};
struct AnalogAttrs {
zb_bool_t out_of_service;
float present_value;
zb_uint8_t status_flags;
zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE];
float max_present_value;
float min_present_value;
float resolution;
};
class ZigbeeComponent : public Component {
public:
void setup() override;
void dump_config() override;
void add_callback(zb_uint8_t endpoint, std::function<void(zb_bufid_t bufid)> &&cb) {
// endpoints are enumerated from 1
this->callbacks_[endpoint - 1] = std::move(cb);
}
void add_join_callback(std::function<void()> &&cb) { this->join_cb_.add(std::move(cb)); }
void zboss_signal_handler_esphome(zb_bufid_t bufid);
void factory_reset();
Trigger<> *get_join_trigger() { return &this->join_trigger_; };
void flush();
void loop() override;
protected:
static void zcl_device_cb(zb_bufid_t bufid);
void on_join_();
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
void erase_flash_(int area);
#endif
StaticVector<std::function<void(zb_bufid_t bufid)>, ZIGBEE_ENDPOINTS_COUNT> callbacks_;
CallbackManager<void()> join_cb_;
Trigger<> join_trigger_;
bool need_flush_{false};
};
class ZigbeeEntity {
public:
void set_parent(ZigbeeComponent *parent) { this->parent_ = parent; }
void set_end_point(zb_uint8_t end_point) { this->end_point_ = end_point; }
protected:
zb_uint8_t end_point_{0};
ZigbeeComponent *parent_{nullptr};
};
} // namespace esphome::zigbee
#endif

View File

@@ -0,0 +1,265 @@
from datetime import datetime
from esphome import automation
import esphome.codegen as cg
from esphome.components.zephyr import zephyr_add_prj_conf
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_NAME, __version__
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.cpp_generator import (
AssignmentExpression,
MockObj,
VariableDeclarationExpression,
)
from esphome.types import ConfigType
from .const_zephyr import (
CONF_ON_JOIN,
CONF_WIPE_ON_BOOT,
CONF_ZIGBEE_BINARY_SENSOR,
CONF_ZIGBEE_ID,
KEY_EP_NUMBER,
KEY_ZIGBEE,
ZB_ZCL_BASIC_ATTRS_EXT_T,
ZB_ZCL_CLUSTER_ID_BASIC,
ZB_ZCL_CLUSTER_ID_BINARY_INPUT,
ZB_ZCL_CLUSTER_ID_IDENTIFY,
ZB_ZCL_IDENTIFY_ATTRS_T,
BinaryAttrs,
ZigbeeComponent,
zigbee_ns,
)
ZigbeeBinarySensor = zigbee_ns.class_("ZigbeeBinarySensor", cg.Component)
zephyr_binary_sensor = cv.Schema(
{
cv.OnlyWith(CONF_ZIGBEE_ID, ["nrf52", "zigbee"]): cv.use_id(ZigbeeComponent),
cv.OnlyWith(CONF_ZIGBEE_BINARY_SENSOR, ["nrf52", "zigbee"]): cv.declare_id(
ZigbeeBinarySensor
),
}
)
async def zephyr_to_code(config: ConfigType) -> None:
zephyr_add_prj_conf("ZIGBEE", True)
zephyr_add_prj_conf("ZIGBEE_APP_UTILS", True)
zephyr_add_prj_conf("ZIGBEE_ROLE_END_DEVICE", True)
zephyr_add_prj_conf("ZIGBEE_CHANNEL_SELECTION_MODE_MULTI", True)
zephyr_add_prj_conf("CRYPTO", True)
zephyr_add_prj_conf("NET_IPV6", False)
zephyr_add_prj_conf("NET_IP_ADDR_CHECK", False)
zephyr_add_prj_conf("NET_UDP", False)
if config[CONF_WIPE_ON_BOOT]:
cg.add_define("USE_ZIGBEE_WIPE_ON_BOOT")
var = cg.new_Pvariable(config[CONF_ID])
if on_join_config := config.get(CONF_ON_JOIN):
await automation.build_automation(var.get_join_trigger(), [], on_join_config)
await cg.register_component(var, config)
await _attr_to_code(config)
CORE.add_job(_ctx_to_code, config)
async def _attr_to_code(config: ConfigType) -> None:
# Create the basic attributes structure and attribute list
basic_attrs = zigbee_new_variable("zigbee_basic_attrs", ZB_ZCL_BASIC_ATTRS_EXT_T)
zigbee_new_attr_list(
"zigbee_basic_attrib_list",
"ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT",
zigbee_assign(basic_attrs.zcl_version, cg.RawExpression("ZB_ZCL_VERSION")),
zigbee_assign(basic_attrs.app_version, 0),
zigbee_assign(basic_attrs.stack_version, 0),
zigbee_assign(basic_attrs.hw_version, 0),
zigbee_set_string(basic_attrs.mf_name, "esphome"),
zigbee_set_string(basic_attrs.model_id, CORE.name),
zigbee_set_string(
basic_attrs.date_code, datetime.now().strftime("%d/%m/%y %H:%M")
),
zigbee_assign(
basic_attrs.power_source,
cg.RawExpression("ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE"),
),
zigbee_set_string(basic_attrs.location_id, ""),
zigbee_assign(
basic_attrs.ph_env, cg.RawExpression("ZB_ZCL_BASIC_ENV_UNSPECIFIED")
),
zigbee_set_string(basic_attrs.sw_ver, __version__),
)
# Create the identify attributes structure and attribute list
identify_attrs = zigbee_new_variable(
"zigbee_identify_attrs", ZB_ZCL_IDENTIFY_ATTRS_T
)
zigbee_new_attr_list(
"zigbee_identify_attrib_list",
"ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST",
zigbee_assign(
identify_attrs.identify_time,
cg.RawExpression("ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE"),
),
)
def zigbee_new_variable(name: str, type_: str) -> cg.MockObj:
"""Create a global variable with the given name and type."""
decl = VariableDeclarationExpression(type_, "", name)
CORE.add_global(decl)
return MockObj(name, ".")
def zigbee_assign(target: cg.MockObj, expression: cg.RawExpression | int) -> str:
"""Assign an expression to a target and return a reference to it."""
cg.add(AssignmentExpression("", "", target, expression))
return f"&{target}"
def zigbee_set_string(target: cg.MockObj, value: str) -> str:
"""Set a ZCL string value and return the target name (arrays decay to pointers)."""
cg.add(
cg.RawExpression(
f"ZB_ZCL_SET_STRING_VAL({target}, {cg.safe_exp(value)}, ZB_ZCL_STRING_CONST_SIZE({cg.safe_exp(value)}))"
)
)
return str(target)
def zigbee_new_attr_list(name: str, macro: str, *args: str) -> str:
"""Create an attribute list using a ZBOSS macro and return the name."""
obj = cg.RawExpression(f"{macro}({name}, {', '.join(args)})")
CORE.add_global(obj)
return name
class ZigbeeClusterDesc:
"""Represents a Zigbee cluster descriptor for code generation."""
def __init__(self, cluster_id: str, attr_list_name: str | None = None) -> None:
self._cluster_id = cluster_id
self._attr_list_name = attr_list_name
@property
def cluster_id(self) -> str:
return self._cluster_id
@property
def has_attrs(self) -> bool:
return self._attr_list_name is not None
def __str__(self) -> str:
role = (
"ZB_ZCL_CLUSTER_SERVER_ROLE"
if self._attr_list_name
else "ZB_ZCL_CLUSTER_CLIENT_ROLE"
)
if self._attr_list_name:
attr_count = f"ZB_ZCL_ARRAY_SIZE({self._attr_list_name}, zb_zcl_attr_t)"
return f"ZB_ZCL_CLUSTER_DESC({self._cluster_id}, {attr_count}, {self._attr_list_name}, {role}, ZB_ZCL_MANUF_CODE_INVALID)"
return f"ZB_ZCL_CLUSTER_DESC({self._cluster_id}, 0, NULL, {role}, ZB_ZCL_MANUF_CODE_INVALID)"
def zigbee_new_cluster_list(
name: str, clusters: list[ZigbeeClusterDesc]
) -> tuple[str, list[ZigbeeClusterDesc]]:
"""Create a cluster list array and return its name and the clusters."""
# Always include basic and identify clusters first
all_clusters = [
ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BASIC, "zigbee_basic_attrib_list"),
ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_IDENTIFY, "zigbee_identify_attrib_list"),
]
all_clusters.extend(clusters)
cluster_strs = [str(c) for c in all_clusters]
CORE.add_global(
cg.RawExpression(
f"zb_zcl_cluster_desc_t {name}[] = {{{', '.join(cluster_strs)}}}"
)
)
return (name, all_clusters)
def zigbee_register_ep(
ep_name: str,
cluster_list_name: str,
report_attr_count: int,
clusters: list[ZigbeeClusterDesc],
slot_index: int,
) -> None:
"""Register a Zigbee endpoint."""
in_cluster_num = sum(1 for c in clusters if c.has_attrs)
out_cluster_num = len(clusters) - in_cluster_num
cluster_ids = [c.cluster_id for c in clusters]
# Store endpoint name for device context generation
CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER][slot_index] = ep_name
# Generate the endpoint declaration
ep_id = slot_index + 1 # Endpoints are 1-indexed
obj = cg.RawExpression(
f"ESPHOME_ZB_HA_DECLARE_EP({ep_name}, {ep_id}, {cluster_list_name}, "
f"{in_cluster_num}, {out_cluster_num}, {report_attr_count}, {', '.join(cluster_ids)})"
)
CORE.add_global(obj)
@coroutine_with_priority(CoroPriority.LATE)
async def _ctx_to_code(config: ConfigType) -> None:
cg.add_define("ZIGBEE_ENDPOINTS_COUNT", len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER]))
cg.add_global(
cg.RawExpression(
f"ZBOSS_DECLARE_DEVICE_CTX_EP_VA(zb_device_ctx, &{', &'.join(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])})"
)
)
cg.add(cg.RawExpression("ZB_AF_REGISTER_DEVICE_CTX(&zb_device_ctx)"))
async def zephyr_setup_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
CORE.add_job(_add_binary_sensor, entity, config)
async def _add_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
# Find the next available endpoint slot
slot_index = next(
(i for i, v in enumerate(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER]) if v == ""), None
)
# Create unique names for this sensor's variables based on slot index
prefix = f"zigbee_ep{slot_index + 1}"
attrs_name = f"{prefix}_binary_attrs"
attr_list_name = f"{prefix}_binary_input_attrib_list"
cluster_list_name = f"{prefix}_cluster_list"
ep_name = f"{prefix}_ep"
# Create the binary attributes structure
binary_attrs = zigbee_new_variable(attrs_name, BinaryAttrs)
attr_list = zigbee_new_attr_list(
attr_list_name,
"ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST",
zigbee_assign(binary_attrs.out_of_service, 0),
zigbee_assign(binary_attrs.present_value, 0),
zigbee_assign(binary_attrs.status_flags, 0),
zigbee_set_string(binary_attrs.description, config[CONF_NAME]),
)
# Create cluster list and register endpoint
cluster_list_name, clusters = zigbee_new_cluster_list(
cluster_list_name,
[ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BINARY_INPUT, attr_list)],
)
zigbee_register_ep(ep_name, cluster_list_name, 2, clusters, slot_index)
# Create the ZigbeeBinarySensor component
var = cg.new_Pvariable(config[CONF_ZIGBEE_BINARY_SENSOR], entity)
await cg.register_component(var, config)
cg.add(var.set_end_point(slot_index + 1))
cg.add(var.set_cluster_attributes(binary_attrs))
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
cg.add(var.set_parent(hub))

View File

@@ -305,6 +305,9 @@
#define USE_NRF52_UICR_ERASE
#define USE_SOFTDEVICE_ID 7
#define USE_SOFTDEVICE_VERSION 1
#define USE_ZIGBEE
#define USE_ZIGBEE_WIPE_ON_BOOT
#define ZIGBEE_ENDPOINTS_COUNT 8
#endif
// Disabled feature flags

View File

@@ -17,6 +17,7 @@ def load_idedata(environment, temp_folder, platformio_ini):
"""
#include <zephyr/kernel.h>
int main() { return 0;}
extern "C" void zboss_signal_handler() {};
""",
encoding="utf-8",
)
@@ -27,6 +28,12 @@ int main() { return 0;}
CONFIG_NEWLIB_LIBC=y
CONFIG_BT=y
CONFIG_ADC=y
#zigbee begin
CONFIG_ZIGBEE=y
CONFIG_CRYPTO=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
#zigbee end
""",
encoding="utf-8",
)
@@ -44,10 +51,11 @@ CONFIG_ADC=y
def extract_defines(command):
define_pattern = re.compile(r"-D\s*([^\s]+)")
ignore_prefixes = ("_ASMLANGUAGE", "NRF_802154_ECB_PRIORITY=")
return [
match
match.replace("\\", "")
for match in define_pattern.findall(command)
if match not in ("_ASMLANGUAGE")
if not any(match.startswith(prefix) for prefix in ignore_prefixes)
]
def find_cxx_path(commands):

View File

@@ -0,0 +1,34 @@
---
binary_sensor:
- platform: template
name: "Garage Door Open 1"
- platform: template
name: "Garage Door Open 2"
- platform: template
name: "Garage Door Open 3"
- platform: template
name: "Garage Door Open 4"
- platform: template
name: "Garage Door Open 5"
- platform: template
name: "Garage Door Open 6"
- platform: template
name: "Garage Door Open 7"
internal: True
- platform: template
name: "Garage Door Open 8"
- platform: template
name: "Garage Door Open 9"
zigbee:
wipe_on_boot: true
on_join:
then:
- logger.log: "Joined network"
output:
- platform: template
id: output_factory
type: binary
write_action:
- zigbee.factory_reset

View File

@@ -0,0 +1 @@
<<: !include common.yaml

View File

@@ -0,0 +1 @@
<<: !include common.yaml

View File

@@ -0,0 +1 @@
<<: !include common.yaml