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:
@@ -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
|
||||
|
||||
@@ -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]):
|
||||
|
||||
@@ -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 ==================
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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_)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_++;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
124
esphome/components/zigbee/__init__.py
Normal file
124
esphome/components/zigbee/__init__.py
Normal 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
|
||||
16
esphome/components/zigbee/automation.h
Normal file
16
esphome/components/zigbee/automation.h
Normal 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
|
||||
24
esphome/components/zigbee/const_zephyr.py
Normal file
24
esphome/components/zigbee/const_zephyr.py
Normal 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"
|
||||
37
esphome/components/zigbee/zigbee_binary_sensor_zephyr.cpp
Normal file
37
esphome/components/zigbee/zigbee_binary_sensor_zephyr.cpp
Normal 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
|
||||
45
esphome/components/zigbee/zigbee_binary_sensor_zephyr.h
Normal file
45
esphome/components/zigbee/zigbee_binary_sensor_zephyr.h
Normal 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
|
||||
190
esphome/components/zigbee/zigbee_zephyr.cpp
Normal file
190
esphome/components/zigbee/zigbee_zephyr.cpp
Normal 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
|
||||
104
esphome/components/zigbee/zigbee_zephyr.h
Normal file
104
esphome/components/zigbee/zigbee_zephyr.h
Normal 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
|
||||
265
esphome/components/zigbee/zigbee_zephyr.py
Normal file
265
esphome/components/zigbee/zigbee_zephyr.py
Normal 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))
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
34
tests/components/zigbee/common.yaml
Normal file
34
tests/components/zigbee/common.yaml
Normal 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
|
||||
1
tests/components/zigbee/test.nrf52-adafruit.yaml
Normal file
1
tests/components/zigbee/test.nrf52-adafruit.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/zigbee/test.nrf52-mcumgr.yaml
Normal file
1
tests/components/zigbee/test.nrf52-mcumgr.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
1
tests/components/zigbee/test.nrf52-xiao-ble.yaml
Normal file
1
tests/components/zigbee/test.nrf52-xiao-ble.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user