mirror of https://github.com/esphome/esphome.git
Merge branch 'dev' into dev
This commit is contained in:
commit
ef533df522
|
@ -1,19 +1,3 @@
|
|||
[metadata]
|
||||
license = MIT
|
||||
license_file = LICENSE
|
||||
platforms = any
|
||||
description = Make creating custom firmwares for ESP32/ESP8266 super easy.
|
||||
long_description = file: README.md
|
||||
keywords = home, automation
|
||||
classifier =
|
||||
Environment :: Console
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: End Users/Desktop
|
||||
License :: OSI Approved :: MIT License
|
||||
Programming Language :: C++
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: Home Automation
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
# Following 4 for black compatibility
|
||||
|
@ -37,25 +21,22 @@ max-line-length = 120
|
|||
# D401 First line should be in imperative mood
|
||||
|
||||
ignore =
|
||||
E501,
|
||||
W503,
|
||||
E203,
|
||||
D202,
|
||||
E501,
|
||||
W503,
|
||||
E203,
|
||||
D202,
|
||||
|
||||
D100,
|
||||
D101,
|
||||
D102,
|
||||
D103,
|
||||
D104,
|
||||
D105,
|
||||
D107,
|
||||
D200,
|
||||
D205,
|
||||
D209,
|
||||
D400,
|
||||
D401,
|
||||
D100,
|
||||
D101,
|
||||
D102,
|
||||
D103,
|
||||
D104,
|
||||
D105,
|
||||
D107,
|
||||
D200,
|
||||
D205,
|
||||
D209,
|
||||
D400,
|
||||
D401,
|
||||
|
||||
exclude = api_pb2.py
|
||||
|
||||
[bdist_wheel]
|
||||
universal = 1
|
|
@ -44,6 +44,6 @@ repos:
|
|||
hooks:
|
||||
- id: pylint
|
||||
name: pylint
|
||||
entry: pylint
|
||||
language: system
|
||||
entry: script/run-in-env.sh pylint
|
||||
language: script
|
||||
types: [python]
|
||||
|
|
|
@ -51,6 +51,8 @@ esphome/components/bang_bang/* @OttoWinter
|
|||
esphome/components/bedjet/* @jhansche
|
||||
esphome/components/bedjet/climate/* @jhansche
|
||||
esphome/components/bedjet/fan/* @jhansche
|
||||
esphome/components/bedjet/sensor/* @javawizard @jhansche
|
||||
esphome/components/beken_spi_led_strip/* @Mat931
|
||||
esphome/components/bh1750/* @OttoWinter
|
||||
esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/bk72xx/* @kuba2k2
|
||||
|
|
|
@ -110,7 +110,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
|
||||
fi; \
|
||||
pip3 install \
|
||||
--break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
|
||||
--break-system-packages --no-cache-dir -e /esphome
|
||||
|
||||
# Settings for dashboard
|
||||
ENV USERNAME="" PASSWORD=""
|
||||
|
@ -160,7 +160,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
|
||||
fi; \
|
||||
pip3 install \
|
||||
--break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
|
||||
--break-system-packages --no-cache-dir -e /esphome
|
||||
|
||||
# Labels
|
||||
LABEL \
|
||||
|
|
|
@ -46,27 +46,27 @@ extern "C"
|
|||
ADCSensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
|
||||
pin_->setup();
|
||||
this->pin_->setup();
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
if (channel1_ != ADC1_CHANNEL_MAX) {
|
||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
||||
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
||||
if (!autorange_) {
|
||||
adc1_config_channel_atten(channel1_, attenuation_);
|
||||
if (!this->autorange_) {
|
||||
adc1_config_channel_atten(this->channel1_, this->attenuation_);
|
||||
}
|
||||
} else if (channel2_ != ADC2_CHANNEL_MAX) {
|
||||
if (!autorange_) {
|
||||
adc2_config_channel_atten(channel2_, attenuation_);
|
||||
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
|
||||
if (!this->autorange_) {
|
||||
adc2_config_channel_atten(this->channel2_, this->attenuation_);
|
||||
}
|
||||
}
|
||||
|
||||
// load characteristics for each attenuation
|
||||
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
|
||||
auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
||||
1100, // default vref
|
||||
&cal_characteristics_[i]);
|
||||
&this->cal_characteristics_[i]);
|
||||
switch (cal_value) {
|
||||
case ESP_ADC_CAL_VAL_EFUSE_VREF:
|
||||
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
|
||||
|
@ -99,27 +99,27 @@ void ADCSensor::dump_config() {
|
|||
#ifdef USE_ADC_SENSOR_VCC
|
||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||
#else
|
||||
LOG_PIN(" Pin: ", pin_);
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
#endif
|
||||
#endif // USE_ESP8266 || USE_LIBRETINY
|
||||
|
||||
#ifdef USE_ESP32
|
||||
LOG_PIN(" Pin: ", pin_);
|
||||
if (autorange_) {
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
if (this->autorange_) {
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
||||
} else {
|
||||
switch (this->attenuation_) {
|
||||
case ADC_ATTEN_DB_0:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
|
||||
break;
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
|
||||
break;
|
||||
case ADC_ATTEN_DB_6:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
|
||||
break;
|
||||
case ADC_ATTEN_DB_12_COMPAT:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 12db");
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 12db");
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
|
@ -134,11 +134,11 @@ void ADCSensor::dump_config() {
|
|||
#ifdef USE_ADC_SENSOR_VCC
|
||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||
#else
|
||||
LOG_PIN(" Pin: ", pin_);
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
#endif // USE_ADC_SENSOR_VCC
|
||||
}
|
||||
#endif // USE_RP2040
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
|
@ -149,14 +149,24 @@ void ADCSensor::update() {
|
|||
this->publish_state(value_v);
|
||||
}
|
||||
|
||||
void ADCSensor::set_sample_count(uint8_t sample_count) {
|
||||
if (sample_count != 0) {
|
||||
this->sample_count_ = sample_count;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
float ADCSensor::sample() {
|
||||
uint32_t raw = 0;
|
||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
int32_t raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
||||
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#else
|
||||
int32_t raw = analogRead(this->pin_->get_pin()); // NOLINT
|
||||
raw += analogRead(this->pin_->get_pin()); // NOLINT
|
||||
#endif
|
||||
if (output_raw_) {
|
||||
}
|
||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||
if (this->output_raw_) {
|
||||
return raw;
|
||||
}
|
||||
return raw / 1024.0f;
|
||||
|
@ -165,53 +175,57 @@ float ADCSensor::sample() {
|
|||
|
||||
#ifdef USE_ESP32
|
||||
float ADCSensor::sample() {
|
||||
if (!autorange_) {
|
||||
int raw = -1;
|
||||
if (channel1_ != ADC1_CHANNEL_MAX) {
|
||||
raw = adc1_get_raw(channel1_);
|
||||
} else if (channel2_ != ADC2_CHANNEL_MAX) {
|
||||
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
|
||||
if (!this->autorange_) {
|
||||
uint32_t sum = 0;
|
||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||
int raw = -1;
|
||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
||||
raw = adc1_get_raw(this->channel1_);
|
||||
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
|
||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
|
||||
}
|
||||
if (raw == -1) {
|
||||
return NAN;
|
||||
}
|
||||
sum += raw;
|
||||
}
|
||||
|
||||
if (raw == -1) {
|
||||
return NAN;
|
||||
sum = (sum + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||
if (this->output_raw_) {
|
||||
return sum;
|
||||
}
|
||||
if (output_raw_) {
|
||||
return raw;
|
||||
}
|
||||
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
|
||||
uint32_t mv = esp_adc_cal_raw_to_voltage(sum, &this->cal_characteristics_[(int32_t) this->attenuation_]);
|
||||
return mv / 1000.0f;
|
||||
}
|
||||
|
||||
int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
|
||||
|
||||
if (channel1_ != ADC1_CHANNEL_MAX) {
|
||||
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_12_COMPAT);
|
||||
raw12 = adc1_get_raw(channel1_);
|
||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_12_COMPAT);
|
||||
raw12 = adc1_get_raw(this->channel1_);
|
||||
if (raw12 < ADC_MAX) {
|
||||
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
|
||||
raw6 = adc1_get_raw(channel1_);
|
||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_6);
|
||||
raw6 = adc1_get_raw(this->channel1_);
|
||||
if (raw6 < ADC_MAX) {
|
||||
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
|
||||
raw2 = adc1_get_raw(channel1_);
|
||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_2_5);
|
||||
raw2 = adc1_get_raw(this->channel1_);
|
||||
if (raw2 < ADC_MAX) {
|
||||
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_0);
|
||||
raw0 = adc1_get_raw(channel1_);
|
||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_0);
|
||||
raw0 = adc1_get_raw(this->channel1_);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (channel2_ != ADC2_CHANNEL_MAX) {
|
||||
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_12_COMPAT);
|
||||
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
|
||||
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
|
||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_12_COMPAT);
|
||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
|
||||
if (raw12 < ADC_MAX) {
|
||||
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
|
||||
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
|
||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_6);
|
||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
|
||||
if (raw6 < ADC_MAX) {
|
||||
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_2_5);
|
||||
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
|
||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_2_5);
|
||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
|
||||
if (raw2 < ADC_MAX) {
|
||||
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_0);
|
||||
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
|
||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_0);
|
||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,10 +235,10 @@ float ADCSensor::sample() {
|
|||
return NAN;
|
||||
}
|
||||
|
||||
uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
|
||||
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
|
||||
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
|
||||
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
|
||||
uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
|
||||
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
|
||||
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
|
||||
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
|
||||
|
||||
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
|
||||
uint32_t c12 = std::min(raw12, ADC_HALF);
|
||||
|
@ -246,8 +260,11 @@ float ADCSensor::sample() {
|
|||
adc_set_temp_sensor_enabled(true);
|
||||
delay(1);
|
||||
adc_select_input(4);
|
||||
|
||||
int32_t raw = adc_read();
|
||||
uint32_t raw = 0;
|
||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||
raw += adc_read();
|
||||
}
|
||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||
adc_set_temp_sensor_enabled(false);
|
||||
if (this->output_raw_) {
|
||||
return raw;
|
||||
|
@ -268,7 +285,11 @@ float ADCSensor::sample() {
|
|||
adc_gpio_init(pin);
|
||||
adc_select_input(pin - 26);
|
||||
|
||||
int32_t raw = adc_read();
|
||||
uint32_t raw = 0;
|
||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||
raw += adc_read();
|
||||
}
|
||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||
|
||||
#ifdef CYW43_USES_VSYS_PIN
|
||||
if (pin == PICO_VSYS_PIN) {
|
||||
|
@ -276,7 +297,7 @@ float ADCSensor::sample() {
|
|||
}
|
||||
#endif // CYW43_USES_VSYS_PIN
|
||||
|
||||
if (output_raw_) {
|
||||
if (this->output_raw_) {
|
||||
return raw;
|
||||
}
|
||||
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
|
||||
|
@ -287,10 +308,19 @@ float ADCSensor::sample() {
|
|||
|
||||
#ifdef USE_LIBRETINY
|
||||
float ADCSensor::sample() {
|
||||
if (output_raw_) {
|
||||
return analogRead(this->pin_->get_pin()); // NOLINT
|
||||
uint32_t raw = 0;
|
||||
if (this->output_raw_) {
|
||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||
raw += analogRead(this->pin_->get_pin()); // NOLINT
|
||||
}
|
||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||
return raw;
|
||||
}
|
||||
return analogReadVoltage(this->pin_->get_pin()) / 1000.0f; // NOLINT
|
||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||
raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT
|
||||
}
|
||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||
return raw / 1000.0f;
|
||||
}
|
||||
#endif // USE_LIBRETINY
|
||||
|
||||
|
|
|
@ -33,16 +33,16 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||
public:
|
||||
#ifdef USE_ESP32
|
||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
|
||||
void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||
void set_channel1(adc1_channel_t channel) {
|
||||
channel1_ = channel;
|
||||
channel2_ = ADC2_CHANNEL_MAX;
|
||||
this->channel1_ = channel;
|
||||
this->channel2_ = ADC2_CHANNEL_MAX;
|
||||
}
|
||||
void set_channel2(adc2_channel_t channel) {
|
||||
channel2_ = channel;
|
||||
channel1_ = ADC1_CHANNEL_MAX;
|
||||
this->channel2_ = channel;
|
||||
this->channel1_ = ADC1_CHANNEL_MAX;
|
||||
}
|
||||
void set_autorange(bool autorange) { autorange_ = autorange; }
|
||||
void set_autorange(bool autorange) { this->autorange_ = autorange; }
|
||||
#endif
|
||||
|
||||
/// Update ADC values
|
||||
|
@ -53,7 +53,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||
/// `HARDWARE_LATE` setup priority
|
||||
float get_setup_priority() const override;
|
||||
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
|
||||
void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
|
||||
void set_sample_count(uint8_t sample_count);
|
||||
float sample() override;
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
@ -61,12 +62,13 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
void set_is_temperature() { is_temperature_ = true; }
|
||||
void set_is_temperature() { this->is_temperature_ = true; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
InternalGPIOPin *pin_;
|
||||
bool output_raw_{false};
|
||||
uint8_t sample_count_{1};
|
||||
|
||||
#ifdef USE_RP2040
|
||||
bool is_temperature_{false};
|
||||
|
|
|
@ -29,6 +29,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
AUTO_LOAD = ["voltage_sampler"]
|
||||
|
||||
CONF_SAMPLES = "samples"
|
||||
|
||||
|
||||
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
|
||||
|
||||
|
@ -37,6 +39,10 @@ def validate_config(config):
|
|||
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
|
||||
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
|
||||
|
||||
if config.get(CONF_ATTENUATION, None) == "auto" and config.get(CONF_SAMPLES, 1) > 1:
|
||||
raise cv.Invalid(
|
||||
"Automatic attenuation cannot be used when multisampling is set"
|
||||
)
|
||||
if config.get(CONF_ATTENUATION) == "11db":
|
||||
_LOGGER.warning(
|
||||
"`attenuation: 11db` is deprecated, use `attenuation: 12db` instead"
|
||||
|
@ -81,6 +87,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
||||
cv.only_on_esp32, _attenuation
|
||||
),
|
||||
cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s")),
|
||||
|
@ -104,6 +111,7 @@ async def to_code(config):
|
|||
cg.add(var.set_pin(pin))
|
||||
|
||||
cg.add(var.set_output_raw(config[CONF_RAW]))
|
||||
cg.add(var.set_sample_count(config[CONF_SAMPLES]))
|
||||
|
||||
if attenuation := config.get(CONF_ATTENUATION):
|
||||
if attenuation == "auto":
|
||||
|
|
|
@ -31,7 +31,7 @@ CONFIG_SCHEMA = (
|
|||
|
||||
BEDJET_CLIENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_BEDJET_ID): cv.use_id(BedJetHub),
|
||||
cv.GenerateID(CONF_BEDJET_ID): cv.use_id(BedJetHub),
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -157,5 +157,11 @@ bool BedjetCodec::compare(const uint8_t *data, uint16_t length) {
|
|||
return explicit_fields_changed;
|
||||
}
|
||||
|
||||
/// Converts a BedJet temp step into degrees Celsius.
|
||||
float bedjet_temp_to_c(uint8_t temp) {
|
||||
// BedJet temp is "C*2"; to get C, divide by 2.
|
||||
return temp / 2.0f;
|
||||
}
|
||||
|
||||
} // namespace bedjet
|
||||
} // namespace esphome
|
||||
|
|
|
@ -187,5 +187,8 @@ class BedjetCodec {
|
|||
BedjetStatusPacket buf_;
|
||||
};
|
||||
|
||||
/// Converts a BedJet temp step into degrees Celsius.
|
||||
float bedjet_temp_to_c(uint8_t temp);
|
||||
|
||||
} // namespace bedjet
|
||||
} // namespace esphome
|
||||
|
|
|
@ -40,6 +40,14 @@ enum BedjetHeatMode {
|
|||
HEAT_MODE_EXTENDED,
|
||||
};
|
||||
|
||||
// Which temperature to use as the climate entity's current temperature reading
|
||||
enum BedjetTemperatureSource {
|
||||
// Use the temperature of the air the BedJet is putting out
|
||||
TEMPERATURE_SOURCE_OUTLET,
|
||||
// Use the ambient temperature of the room the BedJet is in
|
||||
TEMPERATURE_SOURCE_AMBIENT
|
||||
};
|
||||
|
||||
enum BedjetButton : uint8_t {
|
||||
/// Turn BedJet off
|
||||
BTN_OFF = 0x1,
|
||||
|
|
|
@ -7,6 +7,7 @@ from esphome.const import (
|
|||
CONF_HEAT_MODE,
|
||||
CONF_ID,
|
||||
CONF_RECEIVE_TIMEOUT,
|
||||
CONF_TEMPERATURE_SOURCE,
|
||||
CONF_TIME_ID,
|
||||
)
|
||||
from .. import (
|
||||
|
@ -21,10 +22,15 @@ DEPENDENCIES = ["bedjet"]
|
|||
|
||||
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
|
||||
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
|
||||
BedjetTemperatureSource = bedjet_ns.enum("BedjetTemperatureSource")
|
||||
BEDJET_HEAT_MODES = {
|
||||
"heat": BedjetHeatMode.HEAT_MODE_HEAT,
|
||||
"extended": BedjetHeatMode.HEAT_MODE_EXTENDED,
|
||||
}
|
||||
BEDJET_TEMPERATURE_SOURCES = {
|
||||
"outlet": BedjetTemperatureSource.TEMPERATURE_SOURCE_OUTLET,
|
||||
"ambient": BedjetTemperatureSource.TEMPERATURE_SOURCE_AMBIENT,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
climate.CLIMATE_SCHEMA.extend(
|
||||
|
@ -33,6 +39,9 @@ CONFIG_SCHEMA = (
|
|||
cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
|
||||
BEDJET_HEAT_MODES, lower=True
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_SOURCE, default="ambient"): cv.enum(
|
||||
BEDJET_TEMPERATURE_SOURCES, lower=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
|
@ -63,3 +72,4 @@ async def to_code(config):
|
|||
await register_bedjet_child(var, config)
|
||||
|
||||
cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))
|
||||
cg.add(var.set_temperature_source(config[CONF_TEMPERATURE_SOURCE]))
|
||||
|
|
|
@ -8,12 +8,6 @@ namespace bedjet {
|
|||
|
||||
using namespace esphome::climate;
|
||||
|
||||
/// Converts a BedJet temp step into degrees Celsius.
|
||||
float bedjet_temp_to_c(const uint8_t temp) {
|
||||
// BedJet temp is "C*2"; to get C, divide by 2.
|
||||
return temp / 2.0f;
|
||||
}
|
||||
|
||||
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
|
||||
if (fan_step < BEDJET_FAN_SPEED_COUNT)
|
||||
return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step];
|
||||
|
@ -236,9 +230,14 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
|
|||
if (converted_temp > 0)
|
||||
this->target_temperature = converted_temp;
|
||||
|
||||
converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
|
||||
if (converted_temp > 0)
|
||||
if (this->temperature_source_ == TEMPERATURE_SOURCE_OUTLET) {
|
||||
converted_temp = bedjet_temp_to_c(data->actual_temp_step);
|
||||
} else {
|
||||
converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
|
||||
}
|
||||
if (converted_temp > 0) {
|
||||
this->current_temperature = converted_temp;
|
||||
}
|
||||
|
||||
const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(data->fan_step);
|
||||
if (fan_mode_name != nullptr) {
|
||||
|
|
|
@ -28,6 +28,8 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
|
|||
|
||||
/** Sets the default strategy to use for climate::CLIMATE_MODE_HEAT. */
|
||||
void set_heating_mode(BedjetHeatMode mode) { this->heating_mode_ = mode; }
|
||||
/** Sets the temperature source to use for the climate entity's current temperature */
|
||||
void set_temperature_source(BedjetTemperatureSource source) { this->temperature_source_ = source; }
|
||||
|
||||
climate::ClimateTraits traits() override {
|
||||
auto traits = climate::ClimateTraits();
|
||||
|
@ -74,6 +76,7 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
|
|||
void control(const climate::ClimateCall &call) override;
|
||||
|
||||
BedjetHeatMode heating_mode_ = HEAT_MODE_HEAT;
|
||||
BedjetTemperatureSource temperature_source_ = TEMPERATURE_SOURCE_AMBIENT;
|
||||
|
||||
void reset_state_();
|
||||
bool update_status_();
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
from .. import (
|
||||
BEDJET_CLIENT_SCHEMA,
|
||||
bedjet_ns,
|
||||
register_bedjet_child,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CODEOWNERS = ["@jhansche", "@javawizard"]
|
||||
DEPENDENCIES = ["bedjet"]
|
||||
|
||||
CONF_OUTLET_TEMPERATURE = "outlet_temperature"
|
||||
CONF_AMBIENT_TEMPERATURE = "ambient_temperature"
|
||||
|
||||
BedjetSensor = bedjet_ns.class_("BedjetSensor", cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BedjetSensor),
|
||||
cv.Optional(CONF_OUTLET_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_AMBIENT_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
).extend(BEDJET_CLIENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await register_bedjet_child(var, config)
|
||||
|
||||
if outlet_temperature_sensor := config.get(CONF_OUTLET_TEMPERATURE):
|
||||
sensor_var = await sensor.new_sensor(outlet_temperature_sensor)
|
||||
cg.add(var.set_outlet_temperature_sensor(sensor_var))
|
||||
|
||||
if ambient_temperature_sensor := config.get(CONF_AMBIENT_TEMPERATURE):
|
||||
sensor_var = await sensor.new_sensor(ambient_temperature_sensor)
|
||||
cg.add(var.set_ambient_temperature_sensor(sensor_var))
|
|
@ -0,0 +1,34 @@
|
|||
#include "bedjet_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bedjet {
|
||||
|
||||
std::string BedjetSensor::describe() { return "BedJet Sensor"; }
|
||||
|
||||
void BedjetSensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BedJet Sensor:");
|
||||
LOG_SENSOR(" ", "Outlet Temperature", this->outlet_temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Ambient Temperature", this->ambient_temperature_sensor_);
|
||||
}
|
||||
|
||||
void BedjetSensor::on_bedjet_state(bool is_ready) {}
|
||||
|
||||
void BedjetSensor::on_status(const BedjetStatusPacket *data) {
|
||||
if (this->outlet_temperature_sensor_ != nullptr) {
|
||||
float converted_temp = bedjet_temp_to_c(data->actual_temp_step);
|
||||
if (converted_temp > 0) {
|
||||
this->outlet_temperature_sensor_->publish_state(converted_temp);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ambient_temperature_sensor_ != nullptr) {
|
||||
float converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
|
||||
if (converted_temp > 0) {
|
||||
this->ambient_temperature_sensor_->publish_state(converted_temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace bedjet
|
||||
} // namespace esphome
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/bedjet/bedjet_child.h"
|
||||
#include "esphome/components/bedjet/bedjet_codec.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bedjet {
|
||||
|
||||
class BedjetSensor : public BedJetClient, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
||||
void on_status(const BedjetStatusPacket *data) override;
|
||||
void on_bedjet_state(bool is_ready) override;
|
||||
std::string describe() override;
|
||||
|
||||
void set_outlet_temperature_sensor(sensor::Sensor *outlet_temperature_sensor) {
|
||||
this->outlet_temperature_sensor_ = outlet_temperature_sensor;
|
||||
}
|
||||
void set_ambient_temperature_sensor(sensor::Sensor *ambient_temperature_sensor) {
|
||||
this->ambient_temperature_sensor_ = ambient_temperature_sensor;
|
||||
}
|
||||
|
||||
protected:
|
||||
sensor::Sensor *outlet_temperature_sensor_{nullptr};
|
||||
sensor::Sensor *ambient_temperature_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace bedjet
|
||||
} // namespace esphome
|
|
@ -0,0 +1,384 @@
|
|||
#include "led_strip.h"
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
extern "C" {
|
||||
#include "rtos_pub.h"
|
||||
#include "spi.h"
|
||||
#include "arm_arch.h"
|
||||
#include "general_dma_pub.h"
|
||||
#include "gpio_pub.h"
|
||||
#include "icu_pub.h"
|
||||
#undef SPI_DAT
|
||||
#undef SPI_BASE
|
||||
};
|
||||
|
||||
static const uint32_t SPI_TX_DMA_CHANNEL = GDMA_CHANNEL_3;
|
||||
|
||||
// TODO: Check if SPI_PERI_CLK_DCO depends on the chip variant
|
||||
static const uint32_t SPI_PERI_CLK_26M = 26000000;
|
||||
static const uint32_t SPI_PERI_CLK_DCO = 120000000;
|
||||
|
||||
static const uint32_t SPI_BASE = 0x00802700;
|
||||
static const uint32_t SPI_DAT = SPI_BASE + 3 * 4;
|
||||
static const uint32_t SPI_CONFIG = SPI_BASE + 1 * 4;
|
||||
|
||||
static const uint32_t SPI_TX_EN = 1 << 0;
|
||||
static const uint32_t CTRL_NSSMD_3 = 1 << 17;
|
||||
static const uint32_t SPI_TX_FINISH_EN = 1 << 2;
|
||||
static const uint32_t SPI_RX_FINISH_EN = 1 << 3;
|
||||
|
||||
namespace esphome {
|
||||
namespace beken_spi_led_strip {
|
||||
|
||||
static const char *const TAG = "beken_spi_led_strip";
|
||||
|
||||
struct spi_data_t {
|
||||
SemaphoreHandle_t dma_tx_semaphore;
|
||||
volatile bool tx_in_progress;
|
||||
bool first_run;
|
||||
};
|
||||
|
||||
static spi_data_t *spi_data = nullptr;
|
||||
|
||||
static void set_spi_ctrl_register(unsigned long bit, bool val) {
|
||||
uint32_t value = REG_READ(SPI_CTRL);
|
||||
if (val == 0) {
|
||||
value &= ~bit;
|
||||
} else if (val == 1) {
|
||||
value |= bit;
|
||||
}
|
||||
REG_WRITE(SPI_CTRL, value);
|
||||
}
|
||||
|
||||
static void set_spi_config_register(unsigned long bit, bool val) {
|
||||
uint32_t value = REG_READ(SPI_CONFIG);
|
||||
if (val == 0) {
|
||||
value &= ~bit;
|
||||
} else if (val == 1) {
|
||||
value |= bit;
|
||||
}
|
||||
REG_WRITE(SPI_CONFIG, value);
|
||||
}
|
||||
|
||||
void spi_dma_tx_enable(bool enable) {
|
||||
GDMA_CFG_ST en_cfg;
|
||||
set_spi_config_register(SPI_TX_EN, enable ? 1 : 0);
|
||||
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
en_cfg.param = enable ? 1 : 0;
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg);
|
||||
}
|
||||
|
||||
static void spi_set_clock(uint32_t max_hz) {
|
||||
int source_clk = 0;
|
||||
int spi_clk = 0;
|
||||
int div = 0;
|
||||
uint32_t param;
|
||||
if (max_hz > 4333000) {
|
||||
if (max_hz > 30000000) {
|
||||
spi_clk = 30000000;
|
||||
} else {
|
||||
spi_clk = max_hz;
|
||||
}
|
||||
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_DOWN, ¶m);
|
||||
source_clk = SPI_PERI_CLK_DCO;
|
||||
param = PCLK_POSI_SPI;
|
||||
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_DCO, ¶m);
|
||||
param = PWD_SPI_CLK_BIT;
|
||||
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, ¶m);
|
||||
} else {
|
||||
spi_clk = max_hz;
|
||||
#if CFG_XTAL_FREQUENCE
|
||||
source_clk = CFG_XTAL_FREQUENCE;
|
||||
#else
|
||||
source_clk = SPI_PERI_CLK_26M;
|
||||
#endif
|
||||
param = PCLK_POSI_SPI;
|
||||
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, ¶m);
|
||||
}
|
||||
div = ((source_clk >> 1) / spi_clk);
|
||||
if (div < 2) {
|
||||
div = 2;
|
||||
} else if (div >= 255) {
|
||||
div = 255;
|
||||
}
|
||||
param = REG_READ(SPI_CTRL);
|
||||
param &= ~(SPI_CKR_MASK << SPI_CKR_POSI);
|
||||
param |= (div << SPI_CKR_POSI);
|
||||
REG_WRITE(SPI_CTRL, param);
|
||||
ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div);
|
||||
}
|
||||
|
||||
void spi_dma_tx_finish_callback(unsigned int param) {
|
||||
spi_data->tx_in_progress = false;
|
||||
xSemaphoreGive(spi_data->dma_tx_semaphore);
|
||||
spi_dma_tx_enable(0);
|
||||
}
|
||||
|
||||
void BekenSPILEDStripLightOutput::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
|
||||
|
||||
size_t buffer_size = this->get_buffer_size_();
|
||||
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
|
||||
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
this->buf_ = allocator.allocate(buffer_size);
|
||||
if (this->buf_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Cannot allocate LED buffer!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->effect_data_ = allocator.allocate(this->num_leds_);
|
||||
if (this->effect_data_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Cannot allocate effect data!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->dma_buf_ = allocator.allocate(dma_buffer_size);
|
||||
if (this->dma_buf_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Cannot allocate DMA buffer!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
memset(this->buf_, 0, buffer_size);
|
||||
memset(this->effect_data_, 0, this->num_leds_);
|
||||
memset(this->dma_buf_, 0, dma_buffer_size);
|
||||
|
||||
uint32_t value = PCLK_POSI_SPI;
|
||||
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &value);
|
||||
|
||||
value = PWD_SPI_CLK_BIT;
|
||||
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &value);
|
||||
|
||||
if (spi_data != nullptr) {
|
||||
ESP_LOGE(TAG, "SPI device already initialized!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t));
|
||||
if (spi_data == nullptr) {
|
||||
ESP_LOGE(TAG, "Cannot allocate spi_data!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
spi_data->dma_tx_semaphore = xSemaphoreCreateBinary();
|
||||
if (spi_data->dma_tx_semaphore == nullptr) {
|
||||
ESP_LOGE(TAG, "TX Semaphore init faild!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
spi_data->first_run = true;
|
||||
|
||||
set_spi_ctrl_register(MSTEN, 0);
|
||||
set_spi_ctrl_register(BIT_WDTH, 0);
|
||||
spi_set_clock(this->spi_frequency_);
|
||||
set_spi_ctrl_register(CKPOL, 0);
|
||||
set_spi_ctrl_register(CKPHA, 0);
|
||||
set_spi_ctrl_register(MSTEN, 1);
|
||||
set_spi_ctrl_register(SPIEN, 1);
|
||||
|
||||
set_spi_ctrl_register(TXINT_EN, 0);
|
||||
set_spi_ctrl_register(RXINT_EN, 0);
|
||||
set_spi_config_register(SPI_TX_FINISH_EN, 1);
|
||||
set_spi_config_register(SPI_RX_FINISH_EN, 1);
|
||||
set_spi_ctrl_register(RXOVR_EN, 0);
|
||||
set_spi_ctrl_register(TXOVR_EN, 0);
|
||||
|
||||
value = REG_READ(SPI_CTRL);
|
||||
value &= ~CTRL_NSSMD_3;
|
||||
value |= (1 << 17);
|
||||
REG_WRITE(SPI_CTRL, value);
|
||||
|
||||
value = GFUNC_MODE_SPI_DMA;
|
||||
sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value);
|
||||
set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0);
|
||||
|
||||
GDMA_CFG_ST en_cfg;
|
||||
GDMACFG_TPYES_ST init_cfg;
|
||||
memset(&init_cfg, 0, sizeof(GDMACFG_TPYES_ST));
|
||||
|
||||
init_cfg.dstdat_width = 8;
|
||||
init_cfg.srcdat_width = 32;
|
||||
init_cfg.dstptr_incr = 0;
|
||||
init_cfg.srcptr_incr = 1;
|
||||
init_cfg.src_start_addr = this->dma_buf_;
|
||||
init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO
|
||||
init_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
init_cfg.prio = 0; // 10
|
||||
init_cfg.u.type4.src_loop_start_addr = this->dma_buf_;
|
||||
init_cfg.u.type4.src_loop_end_addr = this->dma_buf_ + dma_buffer_size;
|
||||
init_cfg.half_fin_handler = nullptr;
|
||||
init_cfg.fin_handler = spi_dma_tx_finish_callback;
|
||||
init_cfg.src_module = GDMA_X_SRC_DTCM_RD_REQ;
|
||||
init_cfg.dst_module = GDMA_X_DST_GSPI_TX_REQ; // GDMA_X_DST_HSSPI_TX_REQ
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_TYPE4, (void *) &init_cfg);
|
||||
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
en_cfg.param = dma_buffer_size;
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_TRANS_LENGTH, (void *) &en_cfg);
|
||||
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
en_cfg.param = 0;
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_WORK_MODE, (void *) &en_cfg);
|
||||
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||
en_cfg.param = 0;
|
||||
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg);
|
||||
|
||||
spi_dma_tx_enable(0);
|
||||
|
||||
value = REG_READ(SPI_CONFIG);
|
||||
value &= ~(0xFFF << 8);
|
||||
value |= ((dma_buffer_size & 0xFFF) << 8);
|
||||
REG_WRITE(SPI_CONFIG, value);
|
||||
}
|
||||
|
||||
void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency) {
|
||||
this->bit0_ = bit0;
|
||||
this->bit1_ = bit1;
|
||||
this->spi_frequency_ = spi_frequency;
|
||||
}
|
||||
|
||||
void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
|
||||
// protect from refreshing too often
|
||||
uint32_t now = micros();
|
||||
if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
|
||||
// try again next loop iteration, so that this change won't get lost
|
||||
this->schedule_show();
|
||||
return;
|
||||
}
|
||||
this->last_refresh_ = now;
|
||||
this->mark_shown_();
|
||||
|
||||
ESP_LOGVV(TAG, "Writing RGB values to bus...");
|
||||
|
||||
if (spi_data == nullptr) {
|
||||
ESP_LOGE(TAG, "SPI not initialized");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!spi_data->first_run && !xSemaphoreTake(spi_data->dma_tx_semaphore, 10 / portTICK_PERIOD_MS)) {
|
||||
ESP_LOGE(TAG, "Timed out waiting for semaphore");
|
||||
return;
|
||||
}
|
||||
|
||||
if (spi_data->tx_in_progress) {
|
||||
ESP_LOGE(TAG, "tx_in_progress is set");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
spi_data->tx_in_progress = true;
|
||||
|
||||
size_t buffer_size = this->get_buffer_size_();
|
||||
size_t size = 0;
|
||||
uint8_t *psrc = this->buf_;
|
||||
uint8_t *pdest = this->dma_buf_ + 64;
|
||||
// The 64 byte padding is a workaround for a SPI DMA bug where the
|
||||
// output doesn't exactly start at the beginning of dma_buf_
|
||||
|
||||
while (size < buffer_size) {
|
||||
uint8_t b = *psrc;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
*pdest++ = b & (1 << (7 - i)) ? this->bit1_ : this->bit0_;
|
||||
}
|
||||
size++;
|
||||
psrc++;
|
||||
}
|
||||
|
||||
spi_data->first_run = false;
|
||||
spi_dma_tx_enable(1);
|
||||
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index) const {
|
||||
int32_t r = 0, g = 0, b = 0;
|
||||
switch (this->rgb_order_) {
|
||||
case ORDER_RGB:
|
||||
r = 0;
|
||||
g = 1;
|
||||
b = 2;
|
||||
break;
|
||||
case ORDER_RBG:
|
||||
r = 0;
|
||||
g = 2;
|
||||
b = 1;
|
||||
break;
|
||||
case ORDER_GRB:
|
||||
r = 1;
|
||||
g = 0;
|
||||
b = 2;
|
||||
break;
|
||||
case ORDER_GBR:
|
||||
r = 2;
|
||||
g = 0;
|
||||
b = 1;
|
||||
break;
|
||||
case ORDER_BGR:
|
||||
r = 2;
|
||||
g = 1;
|
||||
b = 0;
|
||||
break;
|
||||
case ORDER_BRG:
|
||||
r = 1;
|
||||
g = 2;
|
||||
b = 0;
|
||||
break;
|
||||
}
|
||||
uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
|
||||
uint8_t white = this->is_wrgb_ ? 0 : 3;
|
||||
|
||||
return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
|
||||
this->buf_ + (index * multiplier) + g + this->is_wrgb_,
|
||||
this->buf_ + (index * multiplier) + b + this->is_wrgb_,
|
||||
this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
|
||||
&this->effect_data_[index],
|
||||
&this->correction_};
|
||||
}
|
||||
|
||||
void BekenSPILEDStripLightOutput::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
|
||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||
const char *rgb_order;
|
||||
switch (this->rgb_order_) {
|
||||
case ORDER_RGB:
|
||||
rgb_order = "RGB";
|
||||
break;
|
||||
case ORDER_RBG:
|
||||
rgb_order = "RBG";
|
||||
break;
|
||||
case ORDER_GRB:
|
||||
rgb_order = "GRB";
|
||||
break;
|
||||
case ORDER_GBR:
|
||||
rgb_order = "GBR";
|
||||
break;
|
||||
case ORDER_BGR:
|
||||
rgb_order = "BGR";
|
||||
break;
|
||||
case ORDER_BRG:
|
||||
rgb_order = "BRG";
|
||||
break;
|
||||
default:
|
||||
rgb_order = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
|
||||
ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
|
||||
}
|
||||
|
||||
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
} // namespace beken_spi_led_strip
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_BK72XX
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USE_BK72XX
|
||||
|
||||
#include "esphome/components/light/addressable_light.h"
|
||||
#include "esphome/components/light/light_output.h"
|
||||
#include "esphome/core/color.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace beken_spi_led_strip {
|
||||
|
||||
enum RGBOrder : uint8_t {
|
||||
ORDER_RGB,
|
||||
ORDER_RBG,
|
||||
ORDER_GRB,
|
||||
ORDER_GBR,
|
||||
ORDER_BGR,
|
||||
ORDER_BRG,
|
||||
};
|
||||
|
||||
class BekenSPILEDStripLightOutput : public light::AddressableLight {
|
||||
public:
|
||||
void setup() override;
|
||||
void write_state(light::LightState *state) override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
int32_t size() const override { return this->num_leds_; }
|
||||
light::LightTraits get_traits() override {
|
||||
auto traits = light::LightTraits();
|
||||
if (this->is_rgbw_ || this->is_wrgb_) {
|
||||
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
|
||||
} else {
|
||||
traits.set_supported_color_modes({light::ColorMode::RGB});
|
||||
}
|
||||
return traits;
|
||||
}
|
||||
|
||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
|
||||
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
|
||||
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
|
||||
|
||||
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
||||
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
||||
|
||||
void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency);
|
||||
|
||||
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
|
||||
|
||||
void clear_effect_data() override {
|
||||
for (int i = 0; i < this->size(); i++)
|
||||
this->effect_data_[i] = 0;
|
||||
}
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
light::ESPColorView get_view_internal(int32_t index) const override;
|
||||
|
||||
size_t get_buffer_size_() const { return this->num_leds_ * (this->is_rgbw_ || this->is_wrgb_ ? 4 : 3); }
|
||||
|
||||
uint8_t *buf_{nullptr};
|
||||
uint8_t *effect_data_{nullptr};
|
||||
uint8_t *dma_buf_{nullptr};
|
||||
|
||||
uint8_t pin_;
|
||||
uint16_t num_leds_;
|
||||
bool is_rgbw_;
|
||||
bool is_wrgb_;
|
||||
|
||||
uint32_t spi_frequency_{6666666};
|
||||
uint8_t bit0_{0xE0};
|
||||
uint8_t bit1_{0xFC};
|
||||
RGBOrder rgb_order_;
|
||||
|
||||
uint32_t last_refresh_{0};
|
||||
optional<uint32_t> max_refresh_rate_{};
|
||||
};
|
||||
|
||||
} // namespace beken_spi_led_strip
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_BK72XX
|
|
@ -0,0 +1,134 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import libretiny, light
|
||||
from esphome.const import (
|
||||
CONF_CHIPSET,
|
||||
CONF_IS_RGBW,
|
||||
CONF_MAX_REFRESH_RATE,
|
||||
CONF_NUM_LEDS,
|
||||
CONF_OUTPUT_ID,
|
||||
CONF_PIN,
|
||||
CONF_RGB_ORDER,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@Mat931"]
|
||||
DEPENDENCIES = ["libretiny"]
|
||||
|
||||
beken_spi_led_strip_ns = cg.esphome_ns.namespace("beken_spi_led_strip")
|
||||
BekenSPILEDStripLightOutput = beken_spi_led_strip_ns.class_(
|
||||
"BekenSPILEDStripLightOutput", light.AddressableLight
|
||||
)
|
||||
|
||||
RGBOrder = beken_spi_led_strip_ns.enum("RGBOrder")
|
||||
|
||||
RGB_ORDERS = {
|
||||
"RGB": RGBOrder.ORDER_RGB,
|
||||
"RBG": RGBOrder.ORDER_RBG,
|
||||
"GRB": RGBOrder.ORDER_GRB,
|
||||
"GBR": RGBOrder.ORDER_GBR,
|
||||
"BGR": RGBOrder.ORDER_BGR,
|
||||
"BRG": RGBOrder.ORDER_BRG,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class LEDStripTimings:
|
||||
bit0: int
|
||||
bit1: int
|
||||
spi_frequency: int
|
||||
|
||||
|
||||
CHIPSETS = {
|
||||
"WS2812": LEDStripTimings(
|
||||
0b11100000, 0b11111100, 6666666
|
||||
), # Clock divider: 9, Bit time: 1350ns
|
||||
"SK6812": LEDStripTimings(
|
||||
0b11000000, 0b11111000, 7500000
|
||||
), # Clock divider: 8, Bit time: 1200ns
|
||||
"APA106": LEDStripTimings(
|
||||
0b11000000, 0b11111110, 5454545
|
||||
), # Clock divider: 11, Bit time: 1650ns
|
||||
"SM16703": LEDStripTimings(
|
||||
0b11000000, 0b11111110, 7500000
|
||||
), # Clock divider: 8, Bit time: 1200ns
|
||||
}
|
||||
|
||||
|
||||
CONF_IS_WRGB = "is_wrgb"
|
||||
|
||||
SUPPORTED_PINS = {
|
||||
libretiny.const.FAMILY_BK7231N: [16],
|
||||
libretiny.const.FAMILY_BK7231T: [16],
|
||||
libretiny.const.FAMILY_BK7251: [16],
|
||||
}
|
||||
|
||||
|
||||
def _validate_pin(value):
|
||||
family = libretiny.get_libretiny_family()
|
||||
if family not in SUPPORTED_PINS:
|
||||
raise cv.Invalid(f"Chip family {family} is not supported.")
|
||||
if value not in SUPPORTED_PINS[family]:
|
||||
supported_pin_info = ", ".join(f"{x}" for x in SUPPORTED_PINS[family])
|
||||
raise cv.Invalid(
|
||||
f"Pin {value} is not supported on the {family}. Supported pins: {supported_pin_info}"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def _validate_num_leds(value):
|
||||
max_num_leds = 165 # 170
|
||||
if value[CONF_IS_RGBW] or value[CONF_IS_WRGB]:
|
||||
max_num_leds = 123 # 127
|
||||
if value[CONF_NUM_LEDS] > max_num_leds:
|
||||
raise cv.Invalid(
|
||||
f"The maximum number of LEDs for this configuration is {max_num_leds}.",
|
||||
path=CONF_NUM_LEDS,
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BekenSPILEDStripLightOutput),
|
||||
cv.Required(CONF_PIN): cv.All(
|
||||
pins.internal_gpio_output_pin_number, _validate_pin
|
||||
),
|
||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
||||
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
||||
cv.Optional(CONF_IS_WRGB, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
_validate_num_leds,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
await light.register_light(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
|
||||
if CONF_MAX_REFRESH_RATE in config:
|
||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
chipset = CHIPSETS[config[CONF_CHIPSET]]
|
||||
cg.add(
|
||||
var.set_led_params(
|
||||
chipset.bit0,
|
||||
chipset.bit1,
|
||||
chipset.spi_frequency,
|
||||
)
|
||||
)
|
||||
|
||||
cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
|
||||
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
|
||||
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
|
|
@ -98,6 +98,11 @@ void binary_sensor::MultiClickTrigger::schedule_is_not_valid_(uint32_t max_lengt
|
|||
this->schedule_cooldown_();
|
||||
});
|
||||
}
|
||||
void binary_sensor::MultiClickTrigger::cancel() {
|
||||
ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
|
||||
this->is_valid_ = false;
|
||||
this->schedule_cooldown_();
|
||||
}
|
||||
void binary_sensor::MultiClickTrigger::trigger_() {
|
||||
ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
|
||||
this->at_index_.reset();
|
||||
|
|
|
@ -105,6 +105,8 @@ class MultiClickTrigger : public Trigger<>, public Component {
|
|||
|
||||
void set_invalid_cooldown(uint32_t invalid_cooldown) { this->invalid_cooldown_ = invalid_cooldown; }
|
||||
|
||||
void cancel();
|
||||
|
||||
protected:
|
||||
void on_state_(bool state);
|
||||
void schedule_cooldown_();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
import logging
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
|
@ -8,6 +9,7 @@ from esphome.const import (
|
|||
CONF_NUMBER,
|
||||
CONF_OPEN_DRAIN,
|
||||
CONF_OUTPUT,
|
||||
CONF_IGNORE_PIN_VALIDATION_ERROR,
|
||||
CONF_IGNORE_STRAPPING_WARNING,
|
||||
PLATFORM_ESP32,
|
||||
)
|
||||
|
@ -42,6 +44,9 @@ from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_support
|
|||
ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin)
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _lookup_pin(value):
|
||||
board = CORE.data[KEY_ESP32][KEY_BOARD]
|
||||
board_pins = boards.ESP32_BOARD_PINS.get(board, {})
|
||||
|
@ -111,7 +116,7 @@ _esp32_validations = {
|
|||
}
|
||||
|
||||
|
||||
def validate_gpio_pin(value):
|
||||
def gpio_pin_number_validator(value):
|
||||
value = _translate_pin(value)
|
||||
board = CORE.data[KEY_ESP32][KEY_BOARD]
|
||||
board_pins = boards.ESP32_BOARD_PINS.get(board, {})
|
||||
|
@ -127,7 +132,33 @@ def validate_gpio_pin(value):
|
|||
if variant not in _esp32_validations:
|
||||
raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
|
||||
|
||||
return _esp32_validations[variant].pin_validation(value)
|
||||
return value
|
||||
|
||||
|
||||
def validate_gpio_pin(pin):
|
||||
variant = CORE.data[KEY_ESP32][KEY_VARIANT]
|
||||
if variant not in _esp32_validations:
|
||||
raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
|
||||
|
||||
ignore_pin_validation_warning = pin[CONF_IGNORE_PIN_VALIDATION_ERROR]
|
||||
try:
|
||||
pin[CONF_NUMBER] = _esp32_validations[variant].pin_validation(pin[CONF_NUMBER])
|
||||
except cv.Invalid as exc:
|
||||
if not ignore_pin_validation_warning:
|
||||
raise
|
||||
|
||||
_LOGGER.warning(
|
||||
"Ignoring validation error on pin %d; error: %s",
|
||||
pin[CONF_NUMBER],
|
||||
exc,
|
||||
)
|
||||
else:
|
||||
# Throw an exception if used for a pin that would not have resulted
|
||||
# in a validation error anyway!
|
||||
if ignore_pin_validation_warning:
|
||||
raise cv.Invalid(f"GPIO{pin[CONF_NUMBER]} is not a reserved pin")
|
||||
|
||||
return pin
|
||||
|
||||
|
||||
def validate_supports(value):
|
||||
|
@ -158,9 +189,11 @@ DRIVE_STRENGTHS = {
|
|||
gpio_num_t = cg.global_ns.enum("gpio_num_t")
|
||||
|
||||
CONF_DRIVE_STRENGTH = "drive_strength"
|
||||
|
||||
ESP32_PIN_SCHEMA = cv.All(
|
||||
pins.gpio_base_schema(ESP32InternalGPIOPin, validate_gpio_pin).extend(
|
||||
pins.gpio_base_schema(ESP32InternalGPIOPin, gpio_pin_number_validator).extend(
|
||||
{
|
||||
cv.Optional(CONF_IGNORE_PIN_VALIDATION_ERROR, default=False): cv.boolean,
|
||||
cv.Optional(CONF_IGNORE_STRAPPING_WARNING, default=False): cv.boolean,
|
||||
cv.Optional(CONF_DRIVE_STRENGTH, default="20mA"): cv.All(
|
||||
cv.float_with_unit("current", "mA", optional_unit=True),
|
||||
|
@ -168,6 +201,7 @@ ESP32_PIN_SCHEMA = cv.All(
|
|||
),
|
||||
}
|
||||
),
|
||||
validate_gpio_pin,
|
||||
validate_supports,
|
||||
)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from esphome import pins
|
|||
from esphome.components import esp32_rmt, light
|
||||
from esphome.const import (
|
||||
CONF_CHIPSET,
|
||||
CONF_IS_RGBW,
|
||||
CONF_MAX_REFRESH_RATE,
|
||||
CONF_NUM_LEDS,
|
||||
CONF_OUTPUT_ID,
|
||||
|
@ -52,7 +53,6 @@ CHIPSETS = {
|
|||
}
|
||||
|
||||
|
||||
CONF_IS_RGBW = "is_rgbw"
|
||||
CONF_IS_WRGB = "is_wrgb"
|
||||
CONF_BIT0_HIGH = "bit0_high"
|
||||
CONF_BIT0_LOW = "bit0_low"
|
||||
|
|
|
@ -150,7 +150,7 @@ TOUCH_PAD_WATERPROOF_SHIELD_DRIVER = {
|
|||
|
||||
|
||||
def validate_touch_pad(value):
|
||||
value = gpio.validate_gpio_pin(value)
|
||||
value = gpio.gpio_pin_number_validator(value)
|
||||
variant = get_esp32_variant()
|
||||
if variant not in TOUCH_PADS:
|
||||
raise cv.Invalid(f"ESP32 variant {variant} does not support touch pads.")
|
||||
|
|
|
@ -56,7 +56,7 @@ void IDFI2CBus::setup() {
|
|||
this->mark_failed();
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGV(TAG, "i2c_timeout set to %d ticks (%d us)", timeout_ * 80, timeout_);
|
||||
ESP_LOGV(TAG, "i2c_timeout set to %" PRIu32 " ticks (%" PRIu32 " us)", timeout_ * 80, timeout_);
|
||||
}
|
||||
}
|
||||
err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM);
|
||||
|
|
|
@ -65,8 +65,8 @@ std::string PrometheusHandler::relabel_name_(EntityBase *obj) {
|
|||
// Type-specific implementation
|
||||
#ifdef USE_SENSOR
|
||||
void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_sensor_value GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_sensor_failed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_sensor_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_sensor_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
@ -102,8 +102,8 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
|
|||
// Type-specific implementation
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void PrometheusHandler::binary_sensor_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_binary_sensor_value GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_binary_sensor_failed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_binary_sensor_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_binary_sensor_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
@ -136,10 +136,10 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s
|
|||
|
||||
#ifdef USE_FAN
|
||||
void PrometheusHandler::fan_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_fan_value GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_fan_failed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_fan_speed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_fan_oscillation GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_fan_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_fan_failed gauge\n"));
|
||||
stream->print(F("#TYPE esphome_fan_speed gauge\n"));
|
||||
stream->print(F("#TYPE esphome_fan_oscillation gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
@ -182,9 +182,9 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
|
|||
|
||||
#ifdef USE_LIGHT
|
||||
void PrometheusHandler::light_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_light_state GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_light_color GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_light_effect_active GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_light_state gauge\n"));
|
||||
stream->print(F("#TYPE esphome_light_color gauge\n"));
|
||||
stream->print(F("#TYPE esphome_light_effect_active gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightState *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
@ -259,8 +259,8 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat
|
|||
|
||||
#ifdef USE_COVER
|
||||
void PrometheusHandler::cover_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_cover_value GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_cover_failed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_cover_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_cover_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
@ -302,8 +302,8 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
|
|||
|
||||
#ifdef USE_SWITCH
|
||||
void PrometheusHandler::switch_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_switch_value GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_switch_failed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_switch_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_switch_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
@ -326,8 +326,8 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch
|
|||
|
||||
#ifdef USE_LOCK
|
||||
void PrometheusHandler::lock_type_(AsyncResponseStream *stream) {
|
||||
stream->print(F("#TYPE esphome_lock_value GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_lock_failed GAUGE\n"));
|
||||
stream->print(F("#TYPE esphome_lock_value gauge\n"));
|
||||
stream->print(F("#TYPE esphome_lock_failed gauge\n"));
|
||||
}
|
||||
void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) {
|
||||
if (obj->is_internal() && !this->include_internal_)
|
||||
|
|
|
@ -1913,3 +1913,41 @@ async def abbwelcome_action(var, config, args):
|
|||
cg.add(var.set_data_template(template_))
|
||||
else:
|
||||
cg.add(var.set_data_static(data_))
|
||||
|
||||
|
||||
# Mirage
|
||||
(
|
||||
MirageData,
|
||||
MirageBinarySensor,
|
||||
MirageTrigger,
|
||||
MirageAction,
|
||||
MirageDumper,
|
||||
) = declare_protocol("Mirage")
|
||||
|
||||
MIRAGE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_CODE): cv.All([cv.hex_uint8_t], cv.Length(min=14, max=14)),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@register_binary_sensor("mirage", MirageBinarySensor, MIRAGE_SCHEMA)
|
||||
def mirage_binary_sensor(var, config):
|
||||
cg.add(var.set_code(config[CONF_CODE]))
|
||||
|
||||
|
||||
@register_trigger("mirage", MirageTrigger, MirageData)
|
||||
def mirage_trigger(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_dumper("mirage", MirageDumper)
|
||||
def mirage_dumper(var, config):
|
||||
pass
|
||||
|
||||
|
||||
@register_action("mirage", MirageAction, MIRAGE_SCHEMA)
|
||||
async def mirage_action(var, config, args):
|
||||
vec_ = cg.std_vector.template(cg.uint8)
|
||||
template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_)
|
||||
cg.add(var.set_code(template_))
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
#include "mirage_protocol.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
static const char *const TAG = "remote.mirage";
|
||||
|
||||
constexpr uint32_t HEADER_MARK_US = 8360;
|
||||
constexpr uint32_t HEADER_SPACE_US = 4248;
|
||||
constexpr uint32_t BIT_MARK_US = 554;
|
||||
constexpr uint32_t BIT_ONE_SPACE_US = 1592;
|
||||
constexpr uint32_t BIT_ZERO_SPACE_US = 545;
|
||||
|
||||
constexpr unsigned int MIRAGE_IR_PACKET_BIT_SIZE = 120;
|
||||
|
||||
void MirageProtocol::encode(RemoteTransmitData *dst, const MirageData &data) {
|
||||
ESP_LOGI(TAG, "Transive Mirage: %s", format_hex_pretty(data.data).c_str());
|
||||
dst->set_carrier_frequency(38000);
|
||||
dst->reserve(5 + ((data.data.size() + 1) * 2));
|
||||
dst->mark(HEADER_MARK_US);
|
||||
dst->space(HEADER_SPACE_US);
|
||||
dst->mark(BIT_MARK_US);
|
||||
uint8_t checksum = 0;
|
||||
for (uint8_t item : data.data) {
|
||||
this->encode_byte_(dst, item);
|
||||
checksum += (item >> 4) + (item & 0xF);
|
||||
}
|
||||
this->encode_byte_(dst, checksum);
|
||||
}
|
||||
|
||||
void MirageProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t item) {
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
if (item & (1UL << b)) {
|
||||
dst->space(BIT_ONE_SPACE_US);
|
||||
} else {
|
||||
dst->space(BIT_ZERO_SPACE_US);
|
||||
}
|
||||
dst->mark(BIT_MARK_US);
|
||||
}
|
||||
}
|
||||
|
||||
optional<MirageData> MirageProtocol::decode(RemoteReceiveData src) {
|
||||
if (!src.expect_item(HEADER_MARK_US, HEADER_SPACE_US)) {
|
||||
return {};
|
||||
}
|
||||
if (!src.expect_mark(BIT_MARK_US)) {
|
||||
return {};
|
||||
}
|
||||
size_t size = src.size() - src.get_index() - 1;
|
||||
if (size < MIRAGE_IR_PACKET_BIT_SIZE * 2)
|
||||
return {};
|
||||
size = MIRAGE_IR_PACKET_BIT_SIZE * 2;
|
||||
uint8_t checksum = 0;
|
||||
MirageData out;
|
||||
while (size > 0) {
|
||||
uint8_t data = 0;
|
||||
for (uint8_t b = 0; b < 8; b++) {
|
||||
if (src.expect_space(BIT_ONE_SPACE_US)) {
|
||||
data |= (1UL << b);
|
||||
} else if (!src.expect_space(BIT_ZERO_SPACE_US)) {
|
||||
return {};
|
||||
}
|
||||
if (!src.expect_mark(BIT_MARK_US)) {
|
||||
return {};
|
||||
}
|
||||
size -= 2;
|
||||
}
|
||||
if (size > 0) {
|
||||
checksum += (data >> 4) + (data & 0xF);
|
||||
out.data.push_back(data);
|
||||
} else if (checksum != data) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void MirageProtocol::dump(const MirageData &data) {
|
||||
ESP_LOGI(TAG, "Received Mirage: %s", format_hex_pretty(data.data).c_str());
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace remote_base {
|
||||
|
||||
struct MirageData {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
bool operator==(const MirageData &rhs) const { return data == rhs.data; }
|
||||
};
|
||||
|
||||
class MirageProtocol : public RemoteProtocol<MirageData> {
|
||||
public:
|
||||
void encode(RemoteTransmitData *dst, const MirageData &data) override;
|
||||
optional<MirageData> decode(RemoteReceiveData src) override;
|
||||
void dump(const MirageData &data) override;
|
||||
|
||||
protected:
|
||||
void encode_byte_(RemoteTransmitData *dst, uint8_t item);
|
||||
};
|
||||
|
||||
DECLARE_REMOTE_PROTOCOL(Mirage)
|
||||
|
||||
template<typename... Ts> class MirageAction : public RemoteTransmitterActionBase<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::vector<uint8_t>, code)
|
||||
|
||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||
MirageData data{};
|
||||
data.data = this->code_.value(x...);
|
||||
MirageProtocol().encode(dst, data);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remote_base
|
||||
} // namespace esphome
|
|
@ -6,6 +6,7 @@
|
|||
#include "esphome/core/log.h"
|
||||
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <pico/stdlib.h>
|
||||
|
||||
|
@ -14,6 +15,15 @@ namespace rp2040_pio_led_strip {
|
|||
|
||||
static const char *TAG = "rp2040_pio_led_strip";
|
||||
|
||||
static uint8_t num_instance_[2] = {0, 0};
|
||||
static std::map<Chipset, uint> chipset_offsets_ = {
|
||||
{CHIPSET_WS2812, 0}, {CHIPSET_WS2812B, 0}, {CHIPSET_SK6812, 0}, {CHIPSET_SM16703, 0}, {CHIPSET_CUSTOM, 0},
|
||||
};
|
||||
static std::map<Chipset, bool> conf_count_ = {
|
||||
{CHIPSET_WS2812, false}, {CHIPSET_WS2812B, false}, {CHIPSET_SK6812, false},
|
||||
{CHIPSET_SM16703, false}, {CHIPSET_CUSTOM, false},
|
||||
};
|
||||
|
||||
void RP2040PIOLEDStripLightOutput::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up RP2040 LED Strip...");
|
||||
|
||||
|
@ -34,24 +44,71 @@ void RP2040PIOLEDStripLightOutput::setup() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Initialize the PIO program
|
||||
|
||||
// Select PIO instance to use (0 or 1)
|
||||
this->pio_ = pio0;
|
||||
if (this->pio_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to claim PIO instance");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the assembled program into the PIO and get its location in the PIO's instruction memory
|
||||
uint offset = pio_add_program(this->pio_, this->program_);
|
||||
// if there are multiple strips, we can reuse the same PIO program and save space
|
||||
// but there are only 4 state machines on each PIO so we can only have 4 strips per PIO
|
||||
uint offset = 0;
|
||||
|
||||
if (num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) {
|
||||
ESP_LOGE(TAG, "Too many instances of PIO program");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
// keep track of how many instances of the PIO program are running on each PIO
|
||||
num_instance_[this->pio_ == pio0 ? 0 : 1]++;
|
||||
|
||||
// if there are multiple strips of the same chipset, we can reuse the same PIO program and save space
|
||||
if (this->conf_count_[this->chipset_]) {
|
||||
offset = chipset_offsets_[this->chipset_];
|
||||
} else {
|
||||
// Load the assembled program into the PIO and get its location in the PIO's instruction memory and save it
|
||||
offset = pio_add_program(this->pio_, this->program_);
|
||||
chipset_offsets_[this->chipset_] = offset;
|
||||
conf_count_[this->chipset_] = true;
|
||||
}
|
||||
|
||||
// Configure the state machine's PIO, and start it
|
||||
this->sm_ = pio_claim_unused_sm(this->pio_, true);
|
||||
if (this->sm_ < 0) {
|
||||
// in theory this code should never be reached
|
||||
ESP_LOGE(TAG, "Failed to claim PIO state machine");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initalize the DMA channel (Note: There are 12 DMA channels and 8 state machines so we won't run out)
|
||||
|
||||
this->dma_chan_ = dma_claim_unused_channel(true);
|
||||
if (this->dma_chan_ < 0) {
|
||||
ESP_LOGE(TAG, "Failed to claim DMA channel");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->dma_config_ = dma_channel_get_default_config(this->dma_chan_);
|
||||
channel_config_set_transfer_data_size(
|
||||
&this->dma_config_,
|
||||
DMA_SIZE_8); // 8 bit transfers (could be 32 but the pio program would need to be changed to handle junk data)
|
||||
channel_config_set_read_increment(&this->dma_config_, true); // increment the read address
|
||||
channel_config_set_write_increment(&this->dma_config_, false); // don't increment the write address
|
||||
channel_config_set_dreq(&this->dma_config_,
|
||||
pio_get_dreq(this->pio_, this->sm_, true)); // set the DREQ to the state machine's TX FIFO
|
||||
|
||||
dma_channel_configure(this->dma_chan_, &this->dma_config_,
|
||||
&this->pio_->txf[this->sm_], // write to the state machine's TX FIFO
|
||||
this->buf_, // read from memory
|
||||
this->is_rgbw_ ? num_leds_ * 4 : num_leds_ * 3, // number of bytes to transfer
|
||||
false // don't start yet
|
||||
);
|
||||
|
||||
this->init_(this->pio_, this->sm_, offset, this->pin_, this->max_refresh_rate_);
|
||||
}
|
||||
|
||||
|
@ -68,16 +125,8 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) {
|
|||
return;
|
||||
}
|
||||
|
||||
// assemble bits in buffer to 32 bit words with ex for GBR: 0bGGGGGGGGRRRRRRRRBBBBBBBB00000000
|
||||
for (int i = 0; i < this->num_leds_; i++) {
|
||||
uint8_t multiplier = this->is_rgbw_ ? 4 : 3;
|
||||
uint8_t c1 = this->buf_[(i * multiplier) + 0];
|
||||
uint8_t c2 = this->buf_[(i * multiplier) + 1];
|
||||
uint8_t c3 = this->buf_[(i * multiplier) + 2];
|
||||
uint8_t w = this->is_rgbw_ ? this->buf_[(i * 4) + 3] : 0;
|
||||
uint32_t color = encode_uint32(c1, c2, c3, w);
|
||||
pio_sm_put_blocking(this->pio_, this->sm_, color);
|
||||
}
|
||||
// the bits are already in the correct order for the pio program so we can just copy the buffer using DMA
|
||||
dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_());
|
||||
}
|
||||
|
||||
light::ESPColorView RP2040PIOLEDStripLightOutput::get_view_internal(int32_t index) const {
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
#include "esphome/components/light/addressable_light.h"
|
||||
#include "esphome/components/light/light_output.h"
|
||||
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/structs/pio.h>
|
||||
#include <pico/stdio.h>
|
||||
#include <map>
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040_pio_led_strip {
|
||||
|
@ -25,6 +27,15 @@ enum RGBOrder : uint8_t {
|
|||
ORDER_BRG,
|
||||
};
|
||||
|
||||
enum Chipset : uint8_t {
|
||||
CHIPSET_WS2812,
|
||||
CHIPSET_WS2812B,
|
||||
CHIPSET_SK6812,
|
||||
CHIPSET_SM16703,
|
||||
CHIPSET_APA102,
|
||||
CHIPSET_CUSTOM = 0xFF,
|
||||
};
|
||||
|
||||
inline const char *rgb_order_to_string(RGBOrder order) {
|
||||
switch (order) {
|
||||
case ORDER_RGB:
|
||||
|
@ -69,6 +80,7 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight {
|
|||
void set_program(const pio_program_t *program) { this->program_ = program; }
|
||||
void set_init_function(init_fn init) { this->init_ = init; }
|
||||
|
||||
void set_chipset(Chipset chipset) { this->chipset_ = chipset; };
|
||||
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
|
||||
void clear_effect_data() override {
|
||||
for (int i = 0; i < this->size(); i++) {
|
||||
|
@ -92,14 +104,22 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight {
|
|||
|
||||
pio_hw_t *pio_;
|
||||
uint sm_;
|
||||
uint dma_chan_;
|
||||
dma_channel_config dma_config_;
|
||||
|
||||
RGBOrder rgb_order_{ORDER_RGB};
|
||||
Chipset chipset_{CHIPSET_CUSTOM};
|
||||
|
||||
uint32_t last_refresh_{0};
|
||||
float max_refresh_rate_;
|
||||
|
||||
const pio_program_t *program_;
|
||||
init_fn init_;
|
||||
|
||||
private:
|
||||
inline static int num_instance_[2];
|
||||
inline static std::map<Chipset, bool> conf_count_;
|
||||
inline static std::map<Chipset, int> chipset_offsets_;
|
||||
};
|
||||
|
||||
} // namespace rp2040_pio_led_strip
|
||||
|
|
|
@ -5,6 +5,7 @@ from esphome.components import light, rp2040
|
|||
from esphome.const import (
|
||||
CONF_CHIPSET,
|
||||
CONF_ID,
|
||||
CONF_IS_RGBW,
|
||||
CONF_NUM_LEDS,
|
||||
CONF_OUTPUT_ID,
|
||||
CONF_PIN,
|
||||
|
@ -67,12 +68,15 @@ static inline void rp2040_pio_led_strip_driver_{id}_init(PIO pio, uint sm, uint
|
|||
|
||||
pio_sm_config c = rp2040_pio_led_strip_{id}_program_get_default_config(offset);
|
||||
sm_config_set_set_pins(&c, pin, 1);
|
||||
sm_config_set_out_shift(&c, false, true, {32 if rgbw else 24});
|
||||
sm_config_set_out_shift(&c, false, true, 8);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
|
||||
int cycles_per_bit = 69;
|
||||
float div = 2.409;
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
// target frequency is 57.5MHz
|
||||
long clk = clock_get_hz(clk_sys);
|
||||
long target_freq = 57500000;
|
||||
int n = 2;
|
||||
int f = round(((clk / target_freq) - n ) * 256);
|
||||
sm_config_set_clkdiv_int_frac(&c, n, f);
|
||||
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
|
@ -85,8 +89,9 @@ static inline void rp2040_pio_led_strip_driver_{id}_init(PIO pio, uint sm, uint
|
|||
.wrap_target
|
||||
awaiting_data:
|
||||
; Wait for data in FIFO queue
|
||||
; out null, 24 ; discard the byte lane replication of the FIFO since we only need 8 bits (not needed????)
|
||||
pull block ; this will block until there is data in the FIFO queue and then it will pull it into the shift register
|
||||
set y, {31 if rgbw else 23} ; set y to the number of bits to write counting 0, (23 if RGB, 31 if RGBW)
|
||||
set y, 7 ; set y to the number of bits to write counting 0, (always 7 because we are doing one word at a time)
|
||||
|
||||
mainloop:
|
||||
; go through each bit in the shift register and jump to the appropriate label
|
||||
|
@ -94,7 +99,15 @@ mainloop:
|
|||
|
||||
out x, 1
|
||||
jmp !x, writezero
|
||||
jmp writeone
|
||||
|
||||
writeone:
|
||||
; Write T1H and T1L bits to the output pin
|
||||
set pins, 1 [{t1h}]
|
||||
{nops_t1h}
|
||||
set pins, 0 [{t1l}]
|
||||
{nops_t1l}
|
||||
jmp y--, mainloop
|
||||
jmp awaiting_data
|
||||
|
||||
writezero:
|
||||
; Write T0H and T0L bits to the output pin
|
||||
|
@ -105,14 +118,7 @@ writezero:
|
|||
jmp y--, mainloop
|
||||
jmp awaiting_data
|
||||
|
||||
writeone:
|
||||
; Write T1H and T1L bits to the output pin
|
||||
set pins, 1 [{t1h}]
|
||||
{nops_t1h}
|
||||
set pins, 0 [{t1l}]
|
||||
{nops_t1l}
|
||||
jmp y--, mainloop
|
||||
jmp awaiting_data
|
||||
|
||||
|
||||
.wrap"""
|
||||
|
||||
|
@ -138,7 +144,15 @@ RP2040PIOLEDStripLightOutput = rp2040_pio_led_strip_ns.class_(
|
|||
|
||||
RGBOrder = rp2040_pio_led_strip_ns.enum("RGBOrder")
|
||||
|
||||
Chipsets = rp2040_pio_led_strip_ns.enum("Chipset")
|
||||
Chipset = rp2040_pio_led_strip_ns.enum("Chipset")
|
||||
|
||||
CHIPSETS = {
|
||||
"WS2812": Chipset.CHIPSET_WS2812,
|
||||
"WS2812B": Chipset.CHIPSET_WS2812B,
|
||||
"SK6812": Chipset.CHIPSET_SK6812,
|
||||
"SM16703": Chipset.CHIPSET_SM16703,
|
||||
"CUSTOM": Chipset.CHIPSET_CUSTOM,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -158,14 +172,13 @@ RGB_ORDERS = {
|
|||
"BRG": RGBOrder.ORDER_BRG,
|
||||
}
|
||||
|
||||
CHIPSETS = {
|
||||
"WS2812": LEDStripTimings(20, 43, 41, 31),
|
||||
"WS2812B": LEDStripTimings(23, 46, 46, 23),
|
||||
"SK6812": LEDStripTimings(17, 52, 31, 31),
|
||||
CHIPSET_TIMINGS = {
|
||||
"WS2812": LEDStripTimings(20, 40, 46, 34),
|
||||
"WS2812B": LEDStripTimings(23, 49, 46, 26),
|
||||
"SK6812": LEDStripTimings(17, 52, 34, 34),
|
||||
"SM16703": LEDStripTimings(17, 52, 52, 17),
|
||||
}
|
||||
|
||||
CONF_IS_RGBW = "is_rgbw"
|
||||
CONF_BIT0_HIGH = "bit0_high"
|
||||
CONF_BIT0_LOW = "bit0_low"
|
||||
CONF_BIT1_HIGH = "bit1_high"
|
||||
|
@ -192,7 +205,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||
cv.Required(CONF_PIO): cv.one_of(0, 1, int=True),
|
||||
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
||||
cv.Optional(CONF_CHIPSET): cv.enum(CHIPSETS, upper=True),
|
||||
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
||||
cv.Inclusive(
|
||||
CONF_BIT0_HIGH,
|
||||
|
@ -238,7 +251,8 @@ async def to_code(config):
|
|||
|
||||
key = f"led_strip_{id}"
|
||||
|
||||
if CONF_CHIPSET in config:
|
||||
if chipset := config.get(CONF_CHIPSET):
|
||||
cg.add(var.set_chipset(chipset))
|
||||
_LOGGER.info("Generating PIO assembly code")
|
||||
rp2040.add_pio_file(
|
||||
__name__,
|
||||
|
@ -246,13 +260,14 @@ async def to_code(config):
|
|||
generate_assembly_code(
|
||||
id,
|
||||
config[CONF_IS_RGBW],
|
||||
CHIPSETS[config[CONF_CHIPSET]].T0H,
|
||||
CHIPSETS[config[CONF_CHIPSET]].T0L,
|
||||
CHIPSETS[config[CONF_CHIPSET]].T1H,
|
||||
CHIPSETS[config[CONF_CHIPSET]].T1L,
|
||||
CHIPSET_TIMINGS[chipset].T0H,
|
||||
CHIPSET_TIMINGS[chipset].T0L,
|
||||
CHIPSET_TIMINGS[chipset].T1H,
|
||||
CHIPSET_TIMINGS[chipset].T1L,
|
||||
),
|
||||
)
|
||||
else:
|
||||
cg.add(var.set_chipset(Chipset.CHIPSET_CUSTOM))
|
||||
_LOGGER.info("Generating custom PIO assembly code")
|
||||
rp2040.add_pio_file(
|
||||
__name__,
|
||||
|
|
|
@ -4,7 +4,7 @@ import esphome.config_validation as cv
|
|||
from esphome import core
|
||||
from esphome.const import CONF_SUBSTITUTIONS, VALID_SUBSTITUTIONS_CHARACTERS
|
||||
from esphome.yaml_util import ESPHomeDataBase, make_data_base
|
||||
from esphome.config_helpers import merge_config
|
||||
from esphome.config_helpers import merge_config, Extend, Remove
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -105,7 +105,7 @@ def _substitute_item(substitutions, item, path, ignore_missing):
|
|||
sub = _expand_substitutions(substitutions, item, path, ignore_missing)
|
||||
if sub != item:
|
||||
return sub
|
||||
elif isinstance(item, core.Lambda):
|
||||
elif isinstance(item, (core.Lambda, Extend, Remove)):
|
||||
sub = _expand_substitutions(substitutions, item.value, path, ignore_missing)
|
||||
if sub != item:
|
||||
item.value = sub
|
||||
|
|
|
@ -33,6 +33,7 @@ from esphome.const import (
|
|||
CONF_KEY,
|
||||
CONF_USERNAME,
|
||||
CONF_EAP,
|
||||
CONF_TTLS_PHASE_2,
|
||||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
)
|
||||
|
@ -98,6 +99,14 @@ STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend(
|
|||
}
|
||||
)
|
||||
|
||||
TTLS_PHASE_2 = {
|
||||
"pap": cg.global_ns.ESP_EAP_TTLS_PHASE2_PAP,
|
||||
"chap": cg.global_ns.ESP_EAP_TTLS_PHASE2_CHAP,
|
||||
"mschap": cg.global_ns.ESP_EAP_TTLS_PHASE2_MSCHAP,
|
||||
"mschapv2": cg.global_ns.ESP_EAP_TTLS_PHASE2_MSCHAPV2,
|
||||
"eap": cg.global_ns.ESP_EAP_TTLS_PHASE2_EAP,
|
||||
}
|
||||
|
||||
EAP_AUTH_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
|
@ -105,6 +114,9 @@ EAP_AUTH_SCHEMA = cv.All(
|
|||
cv.Optional(CONF_USERNAME): cv.string_strict,
|
||||
cv.Optional(CONF_PASSWORD): cv.string_strict,
|
||||
cv.Optional(CONF_CERTIFICATE_AUTHORITY): wpa2_eap.validate_certificate,
|
||||
cv.Optional(CONF_TTLS_PHASE_2): cv.All(
|
||||
cv.enum(TTLS_PHASE_2), cv.only_with_esp_idf
|
||||
),
|
||||
cv.Inclusive(
|
||||
CONF_CERTIFICATE, "certificate_and_key"
|
||||
): wpa2_eap.validate_certificate,
|
||||
|
@ -338,6 +350,7 @@ def eap_auth(config):
|
|||
("ca_cert", ca_cert),
|
||||
("client_cert", client_cert),
|
||||
("client_key", key),
|
||||
("ttls_phase_2", config.get(CONF_TTLS_PHASE_2, TTLS_PHASE_2["mschapv2"])),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#include "wifi_component.h"
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include <esp_wpa2.h>
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP_IDF)
|
||||
#include <esp_wifi.h>
|
||||
|
@ -318,6 +323,16 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) {
|
|||
ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str());
|
||||
ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str());
|
||||
ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str());
|
||||
#ifdef USE_ESP_IDF
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
std::map<esp_eap_ttls_phase2_types, std::string> phase2types = {{ESP_EAP_TTLS_PHASE2_PAP, "pap"},
|
||||
{ESP_EAP_TTLS_PHASE2_CHAP, "chap"},
|
||||
{ESP_EAP_TTLS_PHASE2_MSCHAP, "mschap"},
|
||||
{ESP_EAP_TTLS_PHASE2_MSCHAPV2, "mschapv2"},
|
||||
{ESP_EAP_TTLS_PHASE2_EAP, "eap"}};
|
||||
ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), phase2types[eap_config.ttls_phase_2].c_str());
|
||||
#endif
|
||||
#endif
|
||||
bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert);
|
||||
bool client_cert_present = eap_config.client_cert != nullptr && strlen(eap_config.client_cert);
|
||||
bool client_key_present = eap_config.client_key != nullptr && strlen(eap_config.client_key);
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP_IDF) && defined(USE_WIFI_WPA2_EAP)
|
||||
#include <esp_wpa2.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiType.h>
|
||||
|
@ -102,6 +106,10 @@ struct EAPAuth {
|
|||
// used for EAP-TLS
|
||||
const char *client_cert;
|
||||
const char *client_key;
|
||||
// used for EAP-TTLS
|
||||
#ifdef USE_ESP_IDF
|
||||
esp_eap_ttls_phase2_types ttls_phase_2;
|
||||
#endif
|
||||
};
|
||||
#endif // USE_WIFI_WPA2_EAP
|
||||
|
||||
|
|
|
@ -396,6 +396,11 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
|
|||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err);
|
||||
}
|
||||
// set TTLS Phase 2, defaults to MSCHAPV2
|
||||
err = esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(eap.ttls_phase_2);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ttls_phase2_method failed! %d", err);
|
||||
}
|
||||
}
|
||||
err = esp_wifi_sta_wpa2_ent_enable();
|
||||
if (err != ESP_OK) {
|
||||
|
|
|
@ -8,6 +8,9 @@ class Extend:
|
|||
def __str__(self):
|
||||
return f"!extend {self.value}"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Extend({self.value})"
|
||||
|
||||
def __eq__(self, b):
|
||||
"""
|
||||
Check if two Extend objects contain the same ID.
|
||||
|
@ -24,6 +27,9 @@ class Remove:
|
|||
def __str__(self):
|
||||
return f"!remove {self.value}"
|
||||
|
||||
def __repr__(self):
|
||||
return f"Remove({self.value})"
|
||||
|
||||
def __eq__(self, b):
|
||||
"""
|
||||
Check if two Remove objects contain the same ID.
|
||||
|
@ -50,14 +56,19 @@ def merge_config(full_old, full_new):
|
|||
return new
|
||||
res = old.copy()
|
||||
ids = {
|
||||
v[CONF_ID]: i
|
||||
v_id: i
|
||||
for i, v in enumerate(res)
|
||||
if CONF_ID in v and isinstance(v[CONF_ID], str)
|
||||
if (v_id := v.get(CONF_ID)) and isinstance(v_id, str)
|
||||
}
|
||||
extend_ids = {
|
||||
v_id.value: i
|
||||
for i, v in enumerate(res)
|
||||
if (v_id := v.get(CONF_ID)) and isinstance(v_id, Extend)
|
||||
}
|
||||
|
||||
ids_to_delete = []
|
||||
for v in new:
|
||||
if CONF_ID in v:
|
||||
new_id = v[CONF_ID]
|
||||
if new_id := v.get(CONF_ID):
|
||||
if isinstance(new_id, Extend):
|
||||
new_id = new_id.value
|
||||
if new_id in ids:
|
||||
|
@ -69,6 +80,14 @@ def merge_config(full_old, full_new):
|
|||
if new_id in ids:
|
||||
ids_to_delete.append(ids[new_id])
|
||||
continue
|
||||
elif (
|
||||
new_id in extend_ids
|
||||
): # When a package is extending a non-packaged item
|
||||
extend_res = res[extend_ids[new_id]]
|
||||
extend_res[CONF_ID] = new_id
|
||||
new_v = merge(v, extend_res)
|
||||
res[extend_ids[new_id]] = new_v
|
||||
continue
|
||||
else:
|
||||
ids[new_id] = len(res)
|
||||
res.append(v)
|
||||
|
|
|
@ -367,6 +367,7 @@ CONF_IDLE_TIME = "idle_time"
|
|||
CONF_IF = "if"
|
||||
CONF_IGNORE_EFUSE_MAC_CRC = "ignore_efuse_mac_crc"
|
||||
CONF_IGNORE_OUT_OF_RANGE = "ignore_out_of_range"
|
||||
CONF_IGNORE_PIN_VALIDATION_ERROR = "ignore_pin_validation_error"
|
||||
CONF_IGNORE_STRAPPING_WARNING = "ignore_strapping_warning"
|
||||
CONF_IIR_FILTER = "iir_filter"
|
||||
CONF_ILLUMINANCE = "illuminance"
|
||||
|
@ -399,6 +400,7 @@ CONF_INVERT_COLORS = "invert_colors"
|
|||
CONF_INVERTED = "inverted"
|
||||
CONF_IP_ADDRESS = "ip_address"
|
||||
CONF_IRQ_PIN = "irq_pin"
|
||||
CONF_IS_RGBW = "is_rgbw"
|
||||
CONF_JS_INCLUDE = "js_include"
|
||||
CONF_JS_URL = "js_url"
|
||||
CONF_JVC = "jvc"
|
||||
|
@ -855,6 +857,7 @@ CONF_TRANSFORM = "transform"
|
|||
CONF_TRANSITION_LENGTH = "transition_length"
|
||||
CONF_TRIGGER_ID = "trigger_id"
|
||||
CONF_TRIGGER_PIN = "trigger_pin"
|
||||
CONF_TTLS_PHASE_2 = "ttls_phase_2"
|
||||
CONF_TUNE_ANTENNA = "tune_antenna"
|
||||
CONF_TURN_OFF_ACTION = "turn_off_action"
|
||||
CONF_TURN_ON_ACTION = "turn_on_action"
|
||||
|
|
|
@ -327,6 +327,8 @@ def gpio_base_schema(
|
|||
cv.Optional(CONF_MODE, default={}): cv.All(mode_dict, mode_validator),
|
||||
}
|
||||
)
|
||||
|
||||
if invertable:
|
||||
return schema.extend({cv.Optional(CONF_INVERTED, default=False): cv.boolean})
|
||||
|
||||
return schema
|
||||
|
|
30
pylintrc
30
pylintrc
|
@ -1,30 +0,0 @@
|
|||
[MASTER]
|
||||
reports=no
|
||||
ignore=api_pb2.py
|
||||
|
||||
disable=
|
||||
format,
|
||||
missing-docstring,
|
||||
fixme,
|
||||
unused-argument,
|
||||
global-statement,
|
||||
too-few-public-methods,
|
||||
too-many-lines,
|
||||
too-many-locals,
|
||||
too-many-ancestors,
|
||||
too-many-branches,
|
||||
too-many-statements,
|
||||
too-many-arguments,
|
||||
too-many-return-statements,
|
||||
too-many-instance-attributes,
|
||||
duplicate-code,
|
||||
invalid-name,
|
||||
cyclic-import,
|
||||
redefined-builtin,
|
||||
undefined-loop-variable,
|
||||
useless-object-inheritance,
|
||||
stop-iteration-return,
|
||||
import-outside-toplevel,
|
||||
# Broken
|
||||
unsupported-membership-test,
|
||||
unsubscriptable-object,
|
104
pyproject.toml
104
pyproject.toml
|
@ -1,3 +1,107 @@
|
|||
[build-system]
|
||||
requires = ["setuptools==69.2.0", "wheel~=0.43.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "esphome"
|
||||
license = {text = "MIT"}
|
||||
description = "Make creating custom firmwares for ESP32/ESP8266 super easy."
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
{name = "The ESPHome Authors", email = "esphome@nabucasa.com"}
|
||||
]
|
||||
keywords = ["home", "automation"]
|
||||
classifiers = [
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: C++",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Home Automation",
|
||||
]
|
||||
requires-python = ">=3.9.0"
|
||||
|
||||
dynamic = ["dependencies", "optional-dependencies", "version"]
|
||||
|
||||
[project.urls]
|
||||
"Documentation" = "https://esphome.io"
|
||||
"Source Code" = "https://github.com/esphome/esphome"
|
||||
"Bug Tracker" = "https://github.com/esphome/issues/issues"
|
||||
"Feature Request Tracker" = "https://github.com/esphome/feature-requests/issues"
|
||||
"Discord" = "https://discord.gg/KhAMKrd"
|
||||
"Forum" = "https://community.home-assistant.io/c/esphome"
|
||||
"Twitter" = "https://twitter.com/esphome_"
|
||||
|
||||
[project.scripts]
|
||||
esphome = "esphome.__main__:main"
|
||||
|
||||
[tool.setuptools]
|
||||
platforms = ["any"]
|
||||
zip-safe = false
|
||||
include-package-data = true
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
dependencies = {file = ["requirements.txt"]}
|
||||
optional-dependencies.dev = { file = ["requirements_dev.txt"] }
|
||||
optional-dependencies.test = { file = ["requirements_test.txt"] }
|
||||
optional-dependencies.displays = { file = ["requirements_optional.txt"] }
|
||||
version = {attr = "esphome.const.__version__"}
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["esphome*"]
|
||||
|
||||
[tool.black]
|
||||
target-version = ["py39", "py310"]
|
||||
exclude = 'generated'
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
addopts = [
|
||||
"--cov=esphome",
|
||||
"--cov-branch",
|
||||
]
|
||||
|
||||
[tool.pylint.MAIN]
|
||||
py-version = "3.9"
|
||||
ignore = [
|
||||
"api_pb2.py",
|
||||
]
|
||||
persistent = false
|
||||
|
||||
[tool.pylint.REPORTS]
|
||||
score = false
|
||||
|
||||
[tool.pylint."MESSAGES CONTROL"]
|
||||
disable = [
|
||||
"format",
|
||||
"missing-docstring",
|
||||
"fixme",
|
||||
"unused-argument",
|
||||
"global-statement",
|
||||
"too-few-public-methods",
|
||||
"too-many-lines",
|
||||
"too-many-locals",
|
||||
"too-many-ancestors",
|
||||
"too-many-branches",
|
||||
"too-many-statements",
|
||||
"too-many-arguments",
|
||||
"too-many-return-statements",
|
||||
"too-many-instance-attributes",
|
||||
"duplicate-code",
|
||||
"invalid-name",
|
||||
"cyclic-import",
|
||||
"redefined-builtin",
|
||||
"undefined-loop-variable",
|
||||
"useless-object-inheritance",
|
||||
"stop-iteration-return",
|
||||
"import-outside-toplevel",
|
||||
# Broken
|
||||
"unsupported-membership-test",
|
||||
"unsubscriptable-object",
|
||||
]
|
||||
|
||||
[tool.pylint.FORMAT]
|
||||
expected-line-ending-format = "LF"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[pytest]
|
||||
addopts =
|
||||
--cov=esphome
|
||||
--cov-branch
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
my_path=$(git rev-parse --show-toplevel)
|
||||
|
||||
for venv in venv .venv .; do
|
||||
if [ -f "${my_path}/${venv}/bin/activate" ]; then
|
||||
. "${my_path}/${venv}/bin/activate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
exec "$@"
|
|
@ -10,17 +10,17 @@ if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV"
|
|||
if [ -f venv/Scripts/activate ]; then
|
||||
location="venv/Scripts/activate"
|
||||
fi
|
||||
source $location;
|
||||
source $location
|
||||
fi
|
||||
|
||||
# Avoid unsafe git error when running inside devcontainer
|
||||
if [ -n "$DEVCONTAINER" ];then
|
||||
if [ -n "$DEVCONTAINER" ]; then
|
||||
git config --global --add safe.directory "$PWD"
|
||||
fi
|
||||
|
||||
pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt -r requirements_dev.txt
|
||||
pip3 install setuptools wheel
|
||||
pip3 install --no-use-pep517 -e .
|
||||
pip3 install -e --config-settings editable_mode=compat ".[dev,test,displays]"
|
||||
|
||||
pre-commit install
|
||||
|
||||
|
|
82
setup.py
82
setup.py
|
@ -1,82 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""esphome setup script."""
|
||||
import os
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
from esphome import const
|
||||
|
||||
PROJECT_NAME = "esphome"
|
||||
PROJECT_PACKAGE_NAME = "esphome"
|
||||
PROJECT_LICENSE = "MIT"
|
||||
PROJECT_AUTHOR = "ESPHome"
|
||||
PROJECT_COPYRIGHT = "2019, ESPHome"
|
||||
PROJECT_URL = "https://esphome.io/"
|
||||
PROJECT_EMAIL = "esphome@nabucasa.com"
|
||||
|
||||
PROJECT_GITHUB_USERNAME = "esphome"
|
||||
PROJECT_GITHUB_REPOSITORY = "esphome"
|
||||
|
||||
PYPI_URL = f"https://pypi.python.org/pypi/{PROJECT_PACKAGE_NAME}"
|
||||
GITHUB_PATH = f"{PROJECT_GITHUB_USERNAME}/{PROJECT_GITHUB_REPOSITORY}"
|
||||
GITHUB_URL = f"https://github.com/{GITHUB_PATH}"
|
||||
|
||||
DOWNLOAD_URL = f"{GITHUB_URL}/archive/{const.__version__}.zip"
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
with open(os.path.join(here, "requirements.txt")) as requirements_txt:
|
||||
REQUIRES = requirements_txt.read().splitlines()
|
||||
|
||||
with open(os.path.join(here, "README.md")) as readme:
|
||||
LONG_DESCRIPTION = readme.read()
|
||||
|
||||
# If you have problems importing platformio and esptool as modules you can set
|
||||
# $ESPHOME_USE_SUBPROCESS to make ESPHome call their executables instead.
|
||||
# This means they have to be in your $PATH.
|
||||
if "ESPHOME_USE_SUBPROCESS" in os.environ:
|
||||
# Remove platformio and esptool from requirements
|
||||
REQUIRES = [
|
||||
req
|
||||
for req in REQUIRES
|
||||
if not any(req.startswith(prefix) for prefix in ["platformio", "esptool"])
|
||||
]
|
||||
|
||||
CLASSIFIERS = [
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: C++",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Home Automation",
|
||||
]
|
||||
|
||||
setup(
|
||||
name=PROJECT_PACKAGE_NAME,
|
||||
version=const.__version__,
|
||||
license=PROJECT_LICENSE,
|
||||
url=GITHUB_URL,
|
||||
project_urls={
|
||||
"Bug Tracker": "https://github.com/esphome/issues/issues",
|
||||
"Feature Request Tracker": "https://github.com/esphome/feature-requests/issues",
|
||||
"Source Code": "https://github.com/esphome/esphome",
|
||||
"Documentation": "https://esphome.io",
|
||||
"Twitter": "https://twitter.com/esphome_",
|
||||
},
|
||||
download_url=DOWNLOAD_URL,
|
||||
author=PROJECT_AUTHOR,
|
||||
author_email=PROJECT_EMAIL,
|
||||
description="Make creating custom firmwares for ESP32/ESP8266 super easy.",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type="text/markdown",
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
platforms="any",
|
||||
test_suite="tests",
|
||||
python_requires=">=3.9.0",
|
||||
install_requires=REQUIRES,
|
||||
keywords=["home", "automation"],
|
||||
entry_points={"console_scripts": ["esphome = esphome.__main__:main"]},
|
||||
packages=find_packages(include="esphome.*"),
|
||||
)
|
|
@ -26,8 +26,16 @@ climate:
|
|||
name: My Bedjet
|
||||
bedjet_id: bedjet_hub
|
||||
heat_mode: extended
|
||||
temperature_source: ambient
|
||||
|
||||
fan:
|
||||
- platform: bedjet
|
||||
name: My Bedjet fan
|
||||
bedjet_id: bedjet_hub
|
||||
|
||||
sensor:
|
||||
- platform: bedjet
|
||||
ambient_temperature:
|
||||
name: My BedJet Ambient Temperature
|
||||
outlet_temperature:
|
||||
name: My BedJet Outlet Temperature
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
light:
|
||||
- platform: beken_spi_led_strip
|
||||
rgb_order: GRB
|
||||
pin: P16
|
||||
num_leds: 30
|
||||
chipset: ws2812
|
||||
name: "My Light"
|
|
@ -143,6 +143,10 @@ remote_receiver:
|
|||
- logger.log:
|
||||
format: "on_toshiba_ac: %llu %llu"
|
||||
args: ["x.rc_code_1", "x.rc_code_2"]
|
||||
on_mirage:
|
||||
then:
|
||||
- lambda: |-
|
||||
ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str());
|
||||
|
||||
binary_sensor:
|
||||
- platform: remote_receiver
|
||||
|
|
|
@ -142,6 +142,10 @@ remote_receiver:
|
|||
- logger.log:
|
||||
format: "on_toshiba_ac: %llu %llu"
|
||||
args: ["x.rc_code_1", "x.rc_code_2"]
|
||||
on_mirage:
|
||||
then:
|
||||
- lambda: |-
|
||||
ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str());
|
||||
|
||||
binary_sensor:
|
||||
- platform: remote_receiver
|
||||
|
|
|
@ -176,6 +176,11 @@ button:
|
|||
0x00,
|
||||
0x05,
|
||||
]
|
||||
- platform: template
|
||||
name: Mirage
|
||||
on_press:
|
||||
remote_transmitter.transmit_mirage:
|
||||
code: [0x56, 0x77, 0x00, 0x00, 0x22, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
- platform: template
|
||||
name: Dooya
|
||||
on_press:
|
||||
|
|
|
@ -38,6 +38,11 @@ light:
|
|||
id: rgb_led
|
||||
name: "RGB LED"
|
||||
data_rate: 8MHz
|
||||
- platform: binary
|
||||
name: "Red Info Light"
|
||||
output: board_info_ed
|
||||
entity_category: diagnostic
|
||||
restore_mode: ALWAYS_OFF
|
||||
|
||||
spi:
|
||||
id: spi_id_1
|
||||
|
@ -73,6 +78,14 @@ i2c:
|
|||
scl: GPIO18
|
||||
sda: GPIO8
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
id: board_info_ed
|
||||
pin:
|
||||
# This pin is reserved on the ESP32S3!
|
||||
number: 26
|
||||
ignore_pin_validation_error: true
|
||||
|
||||
touchscreen:
|
||||
- platform: tt21100
|
||||
display: displ8
|
||||
|
|
|
@ -3,7 +3,7 @@ esphome:
|
|||
friendly_name: $component_name
|
||||
|
||||
bk72xx:
|
||||
board: cb3s
|
||||
board: generic-bk7231n-qfn32-tuya
|
||||
|
||||
logger:
|
||||
level: VERY_VERBOSE
|
||||
|
|
Loading…
Reference in New Issue