Merge remote-tracking branch 'origin/dev' into mcuboot_support

This commit is contained in:
Tomasz Duda 2024-08-14 19:08:14 +02:00
commit 604c0adba8
107 changed files with 1289 additions and 219 deletions

View file

@ -46,7 +46,7 @@ runs:
- name: Build and push to ghcr by digest - name: Build and push to ghcr by digest
id: build-ghcr id: build-ghcr
uses: docker/build-push-action@v6.6.1 uses: docker/build-push-action@v6.7.0
with: with:
context: . context: .
file: ./docker/Dockerfile file: ./docker/Dockerfile
@ -69,7 +69,7 @@ runs:
- name: Build and push to dockerhub by digest - name: Build and push to dockerhub by digest
id: build-dockerhub id: build-dockerhub
uses: docker/build-push-action@v6.6.1 uses: docker/build-push-action@v6.7.0
with: with:
context: . context: .
file: ./docker/Dockerfile file: ./docker/Dockerfile

View file

@ -9,6 +9,7 @@ on:
paths: paths:
- "**" - "**"
- "!.github/workflows/*.yml" - "!.github/workflows/*.yml"
- "!.github/actions/build-image/*"
- ".github/workflows/ci.yml" - ".github/workflows/ci.yml"
- "!.yamllint" - "!.yamllint"
- "!.github/dependabot.yml" - "!.github/dependabot.yml"

View file

@ -46,6 +46,7 @@ esphome/components/async_tcp/* @OttoWinter
esphome/components/at581x/* @X-Ryl669 esphome/components/at581x/* @X-Ryl669
esphome/components/atc_mithermometer/* @ahpohl esphome/components/atc_mithermometer/* @ahpohl
esphome/components/atm90e26/* @danieltwagner esphome/components/atm90e26/* @danieltwagner
esphome/components/atm90e32/* @circuitsetup @descipher
esphome/components/b_parasite/* @rbaron esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter esphome/components/bang_bang/* @OttoWinter
@ -169,6 +170,8 @@ esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/hm3301/* @freekode esphome/components/hm3301/* @freekode
esphome/components/homeassistant/* @OttoWinter @esphome/core esphome/components/homeassistant/* @OttoWinter @esphome/core
esphome/components/homeassistant/number/* @landonr
esphome/components/homeassistant/switch/* @Links2004
esphome/components/honeywell_hih_i2c/* @Benichou34 esphome/components/honeywell_hih_i2c/* @Benichou34
esphome/components/honeywellabp/* @RubyBailey esphome/components/honeywellabp/* @RubyBailey
esphome/components/honeywellabp2_i2c/* @jpfaff esphome/components/honeywellabp2_i2c/* @jpfaff

View file

@ -14,8 +14,6 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
ESP_LOGD(TAG, "version = %d", value->version); ESP_LOGD(TAG, "version = %d", value->version);
if (value->version == 1) { if (value->version == 1) {
ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
if (this->humidity_sensor_ != nullptr) { if (this->humidity_sensor_ != nullptr) {
this->humidity_sensor_->publish_state(value->humidity / 2.0f); this->humidity_sensor_->publish_state(value->humidity / 2.0f);
} }
@ -43,6 +41,10 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) { if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc); this->tvoc_sensor_->publish_state(value->voc);
} }
if (this->illuminance_sensor_ != nullptr) {
this->illuminance_sensor_->publish_state(value->ambientLight);
}
} else { } else {
ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version); ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
} }
@ -68,6 +70,7 @@ void AirthingsWavePlus::dump_config() {
LOG_SENSOR(" ", "Radon", this->radon_sensor_); LOG_SENSOR(" ", "Radon", this->radon_sensor_);
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_); LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
LOG_SENSOR(" ", "CO2", this->co2_sensor_); LOG_SENSOR(" ", "CO2", this->co2_sensor_);
LOG_SENSOR(" ", "Illuminance", this->illuminance_sensor_);
} }
AirthingsWavePlus::AirthingsWavePlus() { AirthingsWavePlus::AirthingsWavePlus() {

View file

@ -22,6 +22,7 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; } void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; } void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; } void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
void set_illuminance(sensor::Sensor *illuminance) { illuminance_sensor_ = illuminance; }
protected: protected:
bool is_valid_radon_value_(uint16_t radon); bool is_valid_radon_value_(uint16_t radon);
@ -32,6 +33,7 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
sensor::Sensor *radon_sensor_{nullptr}; sensor::Sensor *radon_sensor_{nullptr};
sensor::Sensor *radon_long_term_sensor_{nullptr}; sensor::Sensor *radon_long_term_sensor_{nullptr};
sensor::Sensor *co2_sensor_{nullptr}; sensor::Sensor *co2_sensor_{nullptr};
sensor::Sensor *illuminance_sensor_{nullptr};
struct WavePlusReadings { struct WavePlusReadings {
uint8_t version; uint8_t version;

View file

@ -12,6 +12,9 @@ from esphome.const import (
CONF_CO2, CONF_CO2,
UNIT_BECQUEREL_PER_CUBIC_METER, UNIT_BECQUEREL_PER_CUBIC_METER,
UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_MILLION,
CONF_ILLUMINANCE,
UNIT_LUX,
DEVICE_CLASS_ILLUMINANCE,
) )
DEPENDENCIES = airthings_wave_base.DEPENDENCIES DEPENDENCIES = airthings_wave_base.DEPENDENCIES
@ -45,6 +48,12 @@ CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
device_class=DEVICE_CLASS_CARBON_DIOXIDE, device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_LUX,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
} }
) )
@ -62,3 +71,6 @@ async def to_code(config):
if config_co2 := config.get(CONF_CO2): if config_co2 := config.get(CONF_CO2):
sens = await sensor.new_sensor(config_co2) sens = await sensor.new_sensor(config_co2)
cg.add(var.set_co2(sens)) cg.add(var.set_co2(sens))
if config_illuminance := config.get(CONF_ILLUMINANCE):
sens = await sensor.new_sensor(config_illuminance)
cg.add(var.set_illuminance(sens))

View file

@ -0,0 +1,7 @@
import esphome.codegen as cg
CODEOWNERS = ["@circuitsetup", "@descipher"]
atm90e32_ns = cg.esphome_ns.namespace("atm90e32")
CONF_ATM90E32_ID = "atm90e32_id"

View file

@ -132,10 +132,77 @@ void ATM90E32Component::update() {
this->status_clear_warning(); this->status_clear_warning();
} }
void ATM90E32Component::restore_calibrations_() {
if (enable_offset_calibration_) {
this->pref_.load(&this->offset_phase_);
}
};
void ATM90E32Component::run_offset_calibrations() {
// Run the calibrations and
// Setup voltage and current calibration offsets for PHASE A
this->offset_phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA);
this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_;
this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // C Voltage offset
this->offset_phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA);
this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_;
this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // C Current offset
// Setup voltage and current calibration offsets for PHASE B
this->offset_phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB);
this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_;
this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // C Voltage offset
this->offset_phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB);
this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_;
this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // C Current offset
// Setup voltage and current calibration offsets for PHASE C
this->offset_phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC);
this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_;
this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset
this->offset_phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC);
this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_;
this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset
this->pref_.save(&this->offset_phase_);
ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_,
this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_);
ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_,
this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_);
}
void ATM90E32Component::clear_offset_calibrations() {
// Clear the calibrations and
this->offset_phase_[PHASEA].voltage_offset_ = 0;
this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_;
this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // C Voltage offset
this->offset_phase_[PHASEA].current_offset_ = 0;
this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_;
this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // C Current offset
this->offset_phase_[PHASEB].voltage_offset_ = 0;
this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_;
this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // C Voltage offset
this->offset_phase_[PHASEB].current_offset_ = 0;
this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_;
this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // C Current offset
this->offset_phase_[PHASEC].voltage_offset_ = 0;
this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_;
this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset
this->offset_phase_[PHASEC].current_offset_ = 0;
this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_;
this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset
this->pref_.save(&this->offset_phase_);
ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_,
this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_);
ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_,
this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_);
}
void ATM90E32Component::setup() { void ATM90E32Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component..."); ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
this->spi_setup(); this->spi_setup();
if (this->enable_offset_calibration_) {
uint32_t hash = fnv1_hash(App.get_friendly_name());
this->pref_ = global_preferences->make_preference<Calibration[3]>(hash, true);
this->restore_calibrations_();
}
uint16_t mmode0 = 0x87; // 3P4W 50Hz uint16_t mmode0 = 0x87; // 3P4W 50Hz
if (line_freq_ == 60) { if (line_freq_ == 60) {
mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
@ -167,27 +234,12 @@ void ATM90E32Component::setup() {
this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50% this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750 this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10% this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
// Setup voltage and current calibration offsets for PHASE A
this->phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA);
this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // A Voltage offset
this->phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA);
this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // A Current offset
// Setup voltage and current gain for PHASE A // Setup voltage and current gain for PHASE A
this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_); // A Voltage rms gain this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_); // A Voltage rms gain
this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_); // A line current gain this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_); // A line current gain
// Setup voltage and current calibration offsets for PHASE B
this->phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB);
this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // B Voltage offset
this->phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB);
this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // B Current offset
// Setup voltage and current gain for PHASE B // Setup voltage and current gain for PHASE B
this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_); // B Voltage rms gain this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_); // B Voltage rms gain
this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_); // B line current gain this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_); // B line current gain
// Setup voltage and current calibration offsets for PHASE C
this->phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC);
this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset
this->phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC);
this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset
// Setup voltage and current gain for PHASE C // Setup voltage and current gain for PHASE C
this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_); // C Voltage rms gain this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_); // C Voltage rms gain
this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_); // C line current gain this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_); // C line current gain

View file

@ -1,9 +1,12 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "atm90e32_reg.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/spi/spi.h" #include "esphome/components/spi/spi.h"
#include "atm90e32_reg.h" #include "esphome/core/application.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
namespace esphome { namespace esphome {
namespace atm90e32 { namespace atm90e32 {
@ -20,7 +23,6 @@ class ATM90E32Component : public PollingComponent,
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; float get_setup_priority() const override;
void update() override; void update() override;
void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; } void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; } void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; } void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
@ -48,9 +50,11 @@ class ATM90E32Component : public PollingComponent,
void set_line_freq(int freq) { line_freq_ = freq; } void set_line_freq(int freq) { line_freq_ = freq; }
void set_current_phases(int phases) { current_phases_ = phases; } void set_current_phases(int phases) { current_phases_ = phases; }
void set_pga_gain(uint16_t gain) { pga_gain_ = gain; } void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
void run_offset_calibrations();
void clear_offset_calibrations();
void set_enable_offset_calibration(bool flag) { enable_offset_calibration_ = flag; }
uint16_t calibrate_voltage_offset_phase(uint8_t /*phase*/); uint16_t calibrate_voltage_offset_phase(uint8_t /*phase*/);
uint16_t calibrate_current_offset_phase(uint8_t /*phase*/); uint16_t calibrate_current_offset_phase(uint8_t /*phase*/);
int32_t last_periodic_millis = millis(); int32_t last_periodic_millis = millis();
protected: protected:
@ -83,10 +87,11 @@ class ATM90E32Component : public PollingComponent,
float get_chip_temperature_(); float get_chip_temperature_();
bool get_publish_interval_flag_() { return publish_interval_flag_; }; bool get_publish_interval_flag_() { return publish_interval_flag_; };
void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; }; void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; };
void restore_calibrations_();
struct ATM90E32Phase { struct ATM90E32Phase {
uint16_t voltage_gain_{7305}; uint16_t voltage_gain_{0};
uint16_t ct_gain_{27961}; uint16_t ct_gain_{0};
uint16_t voltage_offset_{0}; uint16_t voltage_offset_{0};
uint16_t current_offset_{0}; uint16_t current_offset_{0};
float voltage_{0}; float voltage_{0};
@ -114,13 +119,21 @@ class ATM90E32Component : public PollingComponent,
uint32_t cumulative_reverse_active_energy_{0}; uint32_t cumulative_reverse_active_energy_{0};
} phase_[3]; } phase_[3];
struct Calibration {
uint16_t voltage_offset_{0};
uint16_t current_offset_{0};
} offset_phase_[3];
ESPPreferenceObject pref_;
sensor::Sensor *freq_sensor_{nullptr}; sensor::Sensor *freq_sensor_{nullptr};
sensor::Sensor *chip_temperature_sensor_{nullptr}; sensor::Sensor *chip_temperature_sensor_{nullptr};
uint16_t pga_gain_{0x15}; uint16_t pga_gain_{0x15};
int line_freq_{60}; int line_freq_{60};
int current_phases_{3}; int current_phases_{3};
bool publish_interval_flag_{true}; bool publish_interval_flag_{false};
bool peak_current_signed_{false}; bool peak_current_signed_{false};
bool enable_offset_calibration_{false};
}; };
} // namespace atm90e32 } // namespace atm90e32

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <cinttypes>
namespace esphome { namespace esphome {
namespace atm90e32 { namespace atm90e32 {

View file

@ -0,0 +1,43 @@
import esphome.codegen as cg
from esphome.components import button
import esphome.config_validation as cv
from esphome.const import CONF_ID, ENTITY_CATEGORY_CONFIG, ICON_CHIP, ICON_SCALE
from .. import atm90e32_ns
from ..sensor import ATM90E32Component
CONF_RUN_OFFSET_CALIBRATION = "run_offset_calibration"
CONF_CLEAR_OFFSET_CALIBRATION = "clear_offset_calibration"
ATM90E32CalibrationButton = atm90e32_ns.class_(
"ATM90E32CalibrationButton",
button.Button,
)
ATM90E32ClearCalibrationButton = atm90e32_ns.class_(
"ATM90E32ClearCalibrationButton",
button.Button,
)
CONFIG_SCHEMA = {
cv.GenerateID(CONF_ID): cv.use_id(ATM90E32Component),
cv.Optional(CONF_RUN_OFFSET_CALIBRATION): button.button_schema(
ATM90E32CalibrationButton,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_SCALE,
),
cv.Optional(CONF_CLEAR_OFFSET_CALIBRATION): button.button_schema(
ATM90E32ClearCalibrationButton,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_CHIP,
),
}
async def to_code(config):
parent = await cg.get_variable(config[CONF_ID])
if run_offset := config.get(CONF_RUN_OFFSET_CALIBRATION):
b = await button.new_button(run_offset)
await cg.register_parented(b, parent)
if clear_offset := config.get(CONF_CLEAR_OFFSET_CALIBRATION):
b = await button.new_button(clear_offset)
await cg.register_parented(b, parent)

View file

@ -0,0 +1,20 @@
#include "atm90e32_button.h"
#include "esphome/core/log.h"
namespace esphome {
namespace atm90e32 {
static const char *const TAG = "atm90e32.button";
void ATM90E32CalibrationButton::press_action() {
ESP_LOGI(TAG, "Running offset calibrations, Note: CTs and ACVs must be 0 during this process...");
this->parent_->run_offset_calibrations();
}
void ATM90E32ClearCalibrationButton::press_action() {
ESP_LOGI(TAG, "Offset calibrations cleared.");
this->parent_->clear_offset_calibrations();
}
} // namespace atm90e32
} // namespace esphome

View file

@ -0,0 +1,27 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/atm90e32/atm90e32.h"
#include "esphome/components/button/button.h"
namespace esphome {
namespace atm90e32 {
class ATM90E32CalibrationButton : public button::Button, public Parented<ATM90E32Component> {
public:
ATM90E32CalibrationButton() = default;
protected:
void press_action() override;
};
class ATM90E32ClearCalibrationButton : public button::Button, public Parented<ATM90E32Component> {
public:
ATM90E32ClearCalibrationButton() = default;
protected:
void press_action() override;
};
} // namespace atm90e32
} // namespace esphome

View file

@ -1,21 +1,21 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi from esphome.components import sensor, spi
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_APPARENT_POWER,
CONF_REACTIVE_POWER,
CONF_VOLTAGE,
CONF_CURRENT, CONF_CURRENT,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_FREQUENCY,
CONF_ID,
CONF_PHASE_A, CONF_PHASE_A,
CONF_PHASE_ANGLE,
CONF_PHASE_B, CONF_PHASE_B,
CONF_PHASE_C, CONF_PHASE_C,
CONF_PHASE_ANGLE,
CONF_POWER, CONF_POWER,
CONF_POWER_FACTOR, CONF_POWER_FACTOR,
CONF_APPARENT_POWER, CONF_REACTIVE_POWER,
CONF_FREQUENCY,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY, CONF_REVERSE_ACTIVE_ENERGY,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
@ -23,13 +23,13 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
ICON_LIGHTBULB,
ICON_CURRENT_AC, ICON_CURRENT_AC,
ICON_LIGHTBULB,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING, STATE_CLASS_TOTAL_INCREASING,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_DEGREES,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_DEGREES,
UNIT_HERTZ, UNIT_HERTZ,
UNIT_VOLT, UNIT_VOLT,
UNIT_VOLT_AMPS_REACTIVE, UNIT_VOLT_AMPS_REACTIVE,
@ -37,6 +37,8 @@ from esphome.const import (
UNIT_WATT_HOURS, UNIT_WATT_HOURS,
) )
from . import atm90e32_ns
CONF_LINE_FREQUENCY = "line_frequency" CONF_LINE_FREQUENCY = "line_frequency"
CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_PGA = "gain_pga" CONF_GAIN_PGA = "gain_pga"
@ -46,6 +48,7 @@ CONF_GAIN_CT = "gain_ct"
CONF_HARMONIC_POWER = "harmonic_power" CONF_HARMONIC_POWER = "harmonic_power"
CONF_PEAK_CURRENT = "peak_current" CONF_PEAK_CURRENT = "peak_current"
CONF_PEAK_CURRENT_SIGNED = "peak_current_signed" CONF_PEAK_CURRENT_SIGNED = "peak_current_signed"
CONF_ENABLE_OFFSET_CALIBRATION = "enable_offset_calibration"
UNIT_DEG = "degrees" UNIT_DEG = "degrees"
LINE_FREQS = { LINE_FREQS = {
"50HZ": 50, "50HZ": 50,
@ -61,7 +64,6 @@ PGA_GAINS = {
"4X": 0x2A, "4X": 0x2A,
} }
atm90e32_ns = cg.esphome_ns.namespace("atm90e32")
ATM90E32Component = atm90e32_ns.class_( ATM90E32Component = atm90e32_ns.class_(
"ATM90E32Component", cg.PollingComponent, spi.SPIDevice "ATM90E32Component", cg.PollingComponent, spi.SPIDevice
) )
@ -164,6 +166,7 @@ CONFIG_SCHEMA = (
), ),
cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True), cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True),
cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean, cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean,
cv.Optional(CONF_ENABLE_OFFSET_CALIBRATION, default=False): cv.boolean,
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
@ -227,3 +230,4 @@ async def to_code(config):
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES])) cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
cg.add(var.set_pga_gain(config[CONF_GAIN_PGA])) cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED])) cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED]))
cg.add(var.set_enable_offset_calibration(config[CONF_ENABLE_OFFSET_CALIBRATION]))

View file

@ -18,11 +18,12 @@ class BinaryLightOutput : public light::LightOutput {
void write_state(light::LightState *state) override { void write_state(light::LightState *state) override {
bool binary; bool binary;
state->current_values_as_binary(&binary); state->current_values_as_binary(&binary);
if (binary) if (binary) {
this->output_->turn_on(); this->output_->turn_on();
else } else {
this->output_->turn_off(); this->output_->turn_off();
} }
}
protected: protected:
output::BinaryOutput *output_; output::BinaryOutput *output_;

View file

@ -16,12 +16,13 @@ class DemoSensor : public sensor::Sensor, public PollingComponent {
float base = std::isnan(this->state) ? 0.0f : this->state; float base = std::isnan(this->state) ? 0.0f : this->state;
this->publish_state(base + val * 10); this->publish_state(base + val * 10);
} else { } else {
if (val < 0.1) if (val < 0.1) {
this->publish_state(NAN); this->publish_state(NAN);
else } else {
this->publish_state(val * 100); this->publish_state(val * 100);
} }
} }
}
}; };
} // namespace demo } // namespace demo

View file

@ -0,0 +1,33 @@
import esphome.codegen as cg
from esphome.components import number
import esphome.config_validation as cv
from .. import (
HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA,
homeassistant_ns,
setup_home_assistant_entity,
)
CODEOWNERS = ["@landonr"]
DEPENDENCIES = ["api"]
HomeassistantNumber = homeassistant_ns.class_(
"HomeassistantNumber", number.Number, cg.Component
)
CONFIG_SCHEMA = (
number.number_schema(HomeassistantNumber)
.extend(HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = await number.new_number(
config,
min_value=0,
max_value=0,
step=0,
)
await cg.register_component(var, config)
setup_home_assistant_entity(var, config)

View file

@ -0,0 +1,100 @@
#include "homeassistant_number.h"
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/log.h"
namespace esphome {
namespace homeassistant {
static const char *const TAG = "homeassistant.number";
void HomeassistantNumber::state_changed_(const std::string &state) {
auto number_value = parse_number<float>(state);
if (!number_value.has_value()) {
ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_.c_str(), state.c_str());
this->publish_state(NAN);
return;
}
if (this->state == number_value.value()) {
return;
}
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), state.c_str());
this->publish_state(number_value.value());
}
void HomeassistantNumber::min_retrieved_(const std::string &min) {
auto min_value = parse_number<float>(min);
if (!min_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_.c_str(), min.c_str());
}
ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str());
this->traits.set_min_value(min_value.value());
}
void HomeassistantNumber::max_retrieved_(const std::string &max) {
auto max_value = parse_number<float>(max);
if (!max_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_.c_str(), max.c_str());
}
ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str());
this->traits.set_max_value(max_value.value());
}
void HomeassistantNumber::step_retrieved_(const std::string &step) {
auto step_value = parse_number<float>(step);
if (!step_value.has_value()) {
ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_.c_str(), step.c_str());
}
ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str());
this->traits.set_step(step_value.value());
}
void HomeassistantNumber::setup() {
api::global_api_server->subscribe_home_assistant_state(
this->entity_id_, nullopt, std::bind(&HomeassistantNumber::state_changed_, this, std::placeholders::_1));
api::global_api_server->get_home_assistant_state(
this->entity_id_, optional<std::string>("min"),
std::bind(&HomeassistantNumber::min_retrieved_, this, std::placeholders::_1));
api::global_api_server->get_home_assistant_state(
this->entity_id_, optional<std::string>("max"),
std::bind(&HomeassistantNumber::max_retrieved_, this, std::placeholders::_1));
api::global_api_server->get_home_assistant_state(
this->entity_id_, optional<std::string>("step"),
std::bind(&HomeassistantNumber::step_retrieved_, this, std::placeholders::_1));
}
void HomeassistantNumber::dump_config() {
LOG_NUMBER("", "Homeassistant Number", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
}
float HomeassistantNumber::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
void HomeassistantNumber::control(float value) {
if (!api::global_api_server->is_connected()) {
ESP_LOGE(TAG, "No clients connected to API server");
return;
}
this->publish_state(value);
api::HomeassistantServiceResponse resp;
resp.service = "number.set_value";
api::HomeassistantServiceMap entity_id;
entity_id.key = "entity_id";
entity_id.value = this->entity_id_;
resp.data.push_back(entity_id);
api::HomeassistantServiceMap entity_value;
entity_value.key = "value";
entity_value.value = to_string(value);
resp.data.push_back(entity_value);
api::global_api_server->send_homeassistant_service_call(resp);
}
} // namespace homeassistant
} // namespace esphome

View file

@ -0,0 +1,31 @@
#pragma once
#include <map>
#include <string>
#include "esphome/components/number/number.h"
#include "esphome/core/component.h"
namespace esphome {
namespace homeassistant {
class HomeassistantNumber : public number::Number, public Component {
public:
void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
void state_changed_(const std::string &state);
void min_retrieved_(const std::string &min);
void max_retrieved_(const std::string &max);
void step_retrieved_(const std::string &step);
void control(float value) override;
std::string entity_id_;
};
} // namespace homeassistant
} // namespace esphome

View file

@ -0,0 +1,30 @@
import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import CONF_ID
from .. import (
HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA,
homeassistant_ns,
setup_home_assistant_entity,
)
CODEOWNERS = ["@Links2004"]
DEPENDENCIES = ["api"]
HomeassistantSwitch = homeassistant_ns.class_(
"HomeassistantSwitch", switch.Switch, cg.Component
)
CONFIG_SCHEMA = (
switch.switch_schema(HomeassistantSwitch)
.extend(cv.COMPONENT_SCHEMA)
.extend(HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await switch.register_switch(var, config)
setup_home_assistant_entity(var, config)

View file

@ -0,0 +1,59 @@
#include "homeassistant_switch.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/log.h"
namespace esphome {
namespace homeassistant {
static const char *const TAG = "homeassistant.switch";
using namespace esphome::switch_;
void HomeassistantSwitch::setup() {
api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullopt, [this](const std::string &state) {
auto val = parse_on_off(state.c_str());
switch (val) {
case PARSE_NONE:
case PARSE_TOGGLE:
ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str());
break;
case PARSE_ON:
case PARSE_OFF:
bool new_state = val == PARSE_ON;
ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state));
this->publish_state(new_state);
break;
}
});
}
void HomeassistantSwitch::dump_config() {
LOG_SWITCH("", "Homeassistant Switch", this);
ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str());
}
float HomeassistantSwitch::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
void HomeassistantSwitch::write_state(bool state) {
if (!api::global_api_server->is_connected()) {
ESP_LOGE(TAG, "No clients connected to API server");
return;
}
api::HomeassistantServiceResponse resp;
if (state) {
resp.service = "switch.turn_on";
} else {
resp.service = "switch.turn_off";
}
api::HomeassistantServiceMap entity_id_kv;
entity_id_kv.key = "entity_id";
entity_id_kv.value = this->entity_id_;
resp.data.push_back(entity_id_kv);
api::global_api_server->send_homeassistant_service_call(resp);
}
} // namespace homeassistant
} // namespace esphome

View file

@ -0,0 +1,22 @@
#pragma once
#include "esphome/components/switch/switch.h"
#include "esphome/core/component.h"
namespace esphome {
namespace homeassistant {
class HomeassistantSwitch : public switch_::Switch, public Component {
public:
void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
void write_state(bool state) override;
std::string entity_id_;
};
} // namespace homeassistant
} // namespace esphome

View file

@ -180,7 +180,11 @@ void I2SAudioSpeaker::player_task(void *params) {
} }
} }
void I2SAudioSpeaker::stop() { void I2SAudioSpeaker::stop() { this->stop_(false); }
void I2SAudioSpeaker::finish() { this->stop_(true); }
void I2SAudioSpeaker::stop_(bool wait_on_empty) {
if (this->is_failed()) if (this->is_failed())
return; return;
if (this->state_ == speaker::STATE_STOPPED) if (this->state_ == speaker::STATE_STOPPED)
@ -192,8 +196,12 @@ void I2SAudioSpeaker::stop() {
this->state_ = speaker::STATE_STOPPING; this->state_ = speaker::STATE_STOPPING;
DataEvent data; DataEvent data;
data.stop = true; data.stop = true;
if (wait_on_empty) {
xQueueSend(this->buffer_queue_, &data, portMAX_DELAY);
} else {
xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY); xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY);
} }
}
void I2SAudioSpeaker::watch_() { void I2SAudioSpeaker::watch_() {
TaskEvent event; TaskEvent event;

View file

@ -53,6 +53,7 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud
void start() override; void start() override;
void stop() override; void stop() override;
void finish() override;
size_t play(const uint8_t *data, size_t length) override; size_t play(const uint8_t *data, size_t length) override;
@ -60,6 +61,7 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud
protected: protected:
void start_(); void start_();
void stop_(bool wait_on_empty);
void watch_(); void watch_();
static void player_task(void *params); static void player_task(void *params);

View file

@ -114,10 +114,11 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
if (now - this->last_add_ < this->add_led_interval_) if (now - this->last_add_ < this->add_led_interval_)
return; return;
this->last_add_ = now; this->last_add_ = now;
if (this->reverse_) if (this->reverse_) {
it.shift_left(1); it.shift_left(1);
else } else {
it.shift_right(1); it.shift_right(1);
}
const AddressableColorWipeEffectColor &color = this->colors_[this->at_color_]; const AddressableColorWipeEffectColor &color = this->colors_[this->at_color_];
Color esp_color = Color(color.r, color.g, color.b, color.w); Color esp_color = Color(color.r, color.g, color.b, color.w);
if (color.gradient) { if (color.gradient) {
@ -127,10 +128,11 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
uint8_t gradient = 255 * ((float) this->leds_added_ / color.num_leds); uint8_t gradient = 255 * ((float) this->leds_added_ / color.num_leds);
esp_color = esp_color.gradient(next_esp_color, gradient); esp_color = esp_color.gradient(next_esp_color, gradient);
} }
if (this->reverse_) if (this->reverse_) {
it[-1] = esp_color; it[-1] = esp_color;
else } else {
it[0] = esp_color; it[0] = esp_color;
}
if (++this->leds_added_ >= color.num_leds) { if (++this->leds_added_ >= color.num_leds) {
this->leds_added_ = 0; this->leds_added_ = 0;
this->at_color_ = (this->at_color_ + 1) % this->colors_.size(); this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
@ -207,10 +209,11 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
const uint8_t sine = half_sin8(view.get_effect_data()); const uint8_t sine = half_sin8(view.get_effect_data());
view = current_color * sine; view = current_color * sine;
const uint8_t new_pos = view.get_effect_data() + pos_add; const uint8_t new_pos = view.get_effect_data() + pos_add;
if (new_pos < view.get_effect_data()) if (new_pos < view.get_effect_data()) {
view.set_effect_data(0); view.set_effect_data(0);
else } else {
view.set_effect_data(new_pos); view.set_effect_data(new_pos);
}
} else { } else {
view = Color::BLACK; view = Color::BLACK;
} }
@ -254,10 +257,11 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect {
view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine); view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine);
} }
const uint8_t new_x = x + pos_add; const uint8_t new_x = x + pos_add;
if (new_x > 0b11111) if (new_x > 0b11111) {
view.set_effect_data(0); view.set_effect_data(0);
else } else {
view.set_effect_data((new_x << 3) | color); view.set_effect_data((new_x << 3) | color);
}
} else { } else {
view = Color(0, 0, 0, 0); view = Color(0, 0, 0, 0);
} }

View file

@ -7,6 +7,8 @@
namespace esphome { namespace esphome {
namespace light { namespace light {
enum class LimitMode { CLAMP, DO_NOTHING };
template<typename... Ts> class ToggleAction : public Action<Ts...> { template<typename... Ts> class ToggleAction : public Action<Ts...> {
public: public:
explicit ToggleAction(LightState *state) : state_(state) {} explicit ToggleAction(LightState *state) : state_(state) {}
@ -77,7 +79,10 @@ template<typename... Ts> class DimRelativeAction : public Action<Ts...> {
float rel = this->relative_brightness_.value(x...); float rel = this->relative_brightness_.value(x...);
float cur; float cur;
this->parent_->remote_values.as_brightness(&cur); this->parent_->remote_values.as_brightness(&cur);
float new_brightness = clamp(cur + rel, 0.0f, 1.0f); if ((limit_mode_ == LimitMode::DO_NOTHING) && ((cur < min_brightness_) || (cur > max_brightness_))) {
return;
}
float new_brightness = clamp(cur + rel, min_brightness_, max_brightness_);
call.set_state(new_brightness != 0.0f); call.set_state(new_brightness != 0.0f);
call.set_brightness(new_brightness); call.set_brightness(new_brightness);
@ -85,8 +90,18 @@ template<typename... Ts> class DimRelativeAction : public Action<Ts...> {
call.perform(); call.perform();
} }
void set_min_max_brightness(float min, float max) {
this->min_brightness_ = min;
this->max_brightness_ = max;
}
void set_limit_mode(LimitMode limit_mode) { this->limit_mode_ = limit_mode; }
protected: protected:
LightState *parent_; LightState *parent_;
float min_brightness_{0.0};
float max_brightness_{1.0};
LimitMode limit_mode_{LimitMode::CLAMP};
}; };
template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> { template<typename... Ts> class LightIsOnCondition : public Condition<Ts...> {

View file

@ -19,10 +19,15 @@ from esphome.const import (
CONF_WARM_WHITE, CONF_WARM_WHITE,
CONF_RANGE_FROM, CONF_RANGE_FROM,
CONF_RANGE_TO, CONF_RANGE_TO,
CONF_BRIGHTNESS_LIMITS,
CONF_LIMIT_MODE,
CONF_MIN_BRIGHTNESS,
CONF_MAX_BRIGHTNESS,
) )
from .types import ( from .types import (
ColorMode, ColorMode,
COLOR_MODES, COLOR_MODES,
LIMIT_MODES,
DimRelativeAction, DimRelativeAction,
ToggleAction, ToggleAction,
LightState, LightState,
@ -167,6 +172,15 @@ LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema(
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable( cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(
cv.positive_time_period_milliseconds cv.positive_time_period_milliseconds
), ),
cv.Optional(CONF_BRIGHTNESS_LIMITS): cv.Schema(
{
cv.Optional(CONF_MIN_BRIGHTNESS, default="0%"): cv.percentage,
cv.Optional(CONF_MAX_BRIGHTNESS, default="100%"): cv.percentage,
cv.Optional(CONF_LIMIT_MODE, default="CLAMP"): cv.enum(
LIMIT_MODES, upper=True, space="_"
),
}
),
} }
) )
@ -182,6 +196,13 @@ async def light_dim_relative_to_code(config, action_id, template_arg, args):
if CONF_TRANSITION_LENGTH in config: if CONF_TRANSITION_LENGTH in config:
templ = await cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) templ = await cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
cg.add(var.set_transition_length(templ)) cg.add(var.set_transition_length(templ))
if conf := config.get(CONF_BRIGHTNESS_LIMITS):
cg.add(
var.set_min_max_brightness(
conf[CONF_MIN_BRIGHTNESS], conf[CONF_MAX_BRIGHTNESS]
)
)
cg.add(var.set_limit_mode(conf[CONF_LIMIT_MODE]))
return var return var

View file

@ -25,7 +25,7 @@ class PulseLightEffect : public LightEffect {
return; return;
} }
auto call = this->state_->turn_on(); auto call = this->state_->turn_on();
float out = this->on_ ? this->max_brightness : this->min_brightness; float out = this->on_ ? this->max_brightness_ : this->min_brightness_;
call.set_brightness_if_supported(out); call.set_brightness_if_supported(out);
call.set_transition_length_if_supported(this->on_ ? this->transition_on_length_ : this->transition_off_length_); call.set_transition_length_if_supported(this->on_ ? this->transition_on_length_ : this->transition_off_length_);
this->on_ = !this->on_; this->on_ = !this->on_;
@ -43,8 +43,8 @@ class PulseLightEffect : public LightEffect {
void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
void set_min_max_brightness(float min, float max) { void set_min_max_brightness(float min, float max) {
this->min_brightness = min; this->min_brightness_ = min;
this->max_brightness = max; this->max_brightness_ = max;
} }
protected: protected:
@ -53,8 +53,8 @@ class PulseLightEffect : public LightEffect {
uint32_t transition_on_length_{}; uint32_t transition_on_length_{};
uint32_t transition_off_length_{}; uint32_t transition_off_length_{};
uint32_t update_interval_{}; uint32_t update_interval_{};
float min_brightness{0.0}; float min_brightness_{0.0};
float max_brightness{1.0}; float max_brightness_{1.0};
}; };
/// Random effect. Sets random colors every 10 seconds and slowly transitions between them. /// Random effect. Sets random colors every 10 seconds and slowly transitions between them.

View file

@ -26,6 +26,13 @@ COLOR_MODES = {
"RGB_COLD_WARM_WHITE": ColorMode.RGB_COLD_WARM_WHITE, "RGB_COLD_WARM_WHITE": ColorMode.RGB_COLD_WARM_WHITE,
} }
# Limit modes
LimitMode = light_ns.enum("LimitMode", is_class=True)
LIMIT_MODES = {
"CLAMP": LimitMode.CLAMP,
"DO_NOTHING": LimitMode.DO_NOTHING,
}
# Actions # Actions
ToggleAction = light_ns.class_("ToggleAction", automation.Action) ToggleAction = light_ns.class_("ToggleAction", automation.Action)
LightControlAction = light_ns.class_("LightControlAction", automation.Action) LightControlAction = light_ns.class_("LightControlAction", automation.Action)

View file

@ -17,6 +17,7 @@ from .defines import (
from .lv_validation import lv_bool, lv_color, lv_image from .lv_validation import lv_bool, lv_color, lv_image
from .lvcode import ( from .lvcode import (
LVGL_COMP_ARG, LVGL_COMP_ARG,
UPDATE_EVENT,
LambdaContext, LambdaContext,
LocalVariable, LocalVariable,
LvConditional, LvConditional,
@ -30,7 +31,6 @@ from .lvcode import (
) )
from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA
from .types import ( from .types import (
LV_EVENT,
LV_STATE, LV_STATE,
LvglAction, LvglAction,
LvglCondition, LvglCondition,
@ -64,7 +64,7 @@ async def update_to_code(config, action_id, template_arg, args):
widget.type.w_type.value_property is not None widget.type.w_type.value_property is not None
and widget.type.w_type.value_property in config and widget.type.w_type.value_property in config
): ):
lv.event_send(widget.obj, LV_EVENT.VALUE_CHANGED, nullptr) lv.event_send(widget.obj, UPDATE_EVENT, nullptr)
widgets = await get_widgets(config[CONF_ID]) widgets = await get_widgets(config[CONF_ID])
return await action_to_code(widgets, do_update, action_id, template_arg, args) return await action_to_code(widgets, do_update, action_id, template_arg, args)

View file

@ -470,6 +470,7 @@ CONF_TOP_LAYER = "top_layer"
CONF_TOUCHSCREENS = "touchscreens" CONF_TOUCHSCREENS = "touchscreens"
CONF_TRANSPARENCY_KEY = "transparency_key" CONF_TRANSPARENCY_KEY = "transparency_key"
CONF_THEME = "theme" CONF_THEME = "theme"
CONF_UPDATE_ON_RELEASE = "update_on_release"
CONF_VISIBLE_ROW_COUNT = "visible_row_count" CONF_VISIBLE_ROW_COUNT = "visible_row_count"
CONF_WIDGET = "widget" CONF_WIDGET = "widget"
CONF_WIDGETS = "widgets" CONF_WIDGETS = "widgets"

View file

@ -38,7 +38,7 @@ class LVLight : public light::LightOutput {
void set_value_(lv_color_t value) { void set_value_(lv_color_t value) {
lv_led_set_color(this->obj_, value); lv_led_set_color(this->obj_, value);
lv_led_on(this->obj_); lv_led_on(this->obj_);
lv_event_send(this->obj_, lv_custom_event, nullptr); lv_event_send(this->obj_, lv_api_event, nullptr);
} }
lv_obj_t *obj_{}; lv_obj_t *obj_{};
optional<lv_color_t> initial_value_{}; optional<lv_color_t> initial_value_{};

View file

@ -29,7 +29,11 @@ LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent)
LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)] LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)]
lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr") lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr")
EVENT_ARG = [(lv_event_t_ptr, "ev")] EVENT_ARG = [(lv_event_t_ptr, "ev")]
CUSTOM_EVENT = literal("lvgl::lv_custom_event") # Two custom events; API_EVENT is fired when an entity is updated remotely by an API interaction;
# UPDATE_EVENT is fired when an entity is programmatically updated locally.
# VALUE_CHANGED is the event generated by LVGL when an entity's value changes through user interaction.
API_EVENT = literal("lvgl::lv_api_event")
UPDATE_EVENT = literal("lvgl::lv_update_event")
def get_line_marks(value) -> list: def get_line_marks(value) -> list:

View file

@ -27,7 +27,8 @@ static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) {
area->y2++; area->y2++;
} }
lv_event_code_t lv_custom_event; // NOLINT lv_event_code_t lv_api_event; // NOLINT
lv_event_code_t lv_update_event; // NOLINT
void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); } void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); }
void LvglComponent::set_paused(bool paused, bool show_snow) { void LvglComponent::set_paused(bool paused, bool show_snow) {
this->paused_ = paused; this->paused_ = paused;
@ -40,15 +41,18 @@ void LvglComponent::set_paused(bool paused, bool show_snow) {
} }
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) {
lv_obj_add_event_cb(obj, callback, event, this); lv_obj_add_event_cb(obj, callback, event, this);
if (event == LV_EVENT_VALUE_CHANGED) {
lv_obj_add_event_cb(obj, callback, lv_custom_event, this);
}
} }
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
lv_event_code_t event2) { lv_event_code_t event2) {
this->add_event_cb(obj, callback, event1); this->add_event_cb(obj, callback, event1);
this->add_event_cb(obj, callback, event2); this->add_event_cb(obj, callback, event2);
} }
void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1,
lv_event_code_t event2, lv_event_code_t event3) {
this->add_event_cb(obj, callback, event1);
this->add_event_cb(obj, callback, event2);
this->add_event_cb(obj, callback, event3);
}
void LvglComponent::add_page(LvPageType *page) { void LvglComponent::add_page(LvPageType *page) {
this->pages_.push_back(page); this->pages_.push_back(page);
page->setup(this->pages_.size() - 1); page->setup(this->pages_.size() - 1);
@ -228,7 +232,8 @@ void LvglComponent::setup() {
lv_log_register_print_cb(log_cb); lv_log_register_print_cb(log_cb);
#endif #endif
lv_init(); lv_init();
lv_custom_event = static_cast<lv_event_code_t>(lv_event_register_id()); lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
auto *display = this->displays_[0]; auto *display = this->displays_[0];
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;

View file

@ -38,7 +38,8 @@
namespace esphome { namespace esphome {
namespace lvgl { namespace lvgl {
extern lv_event_code_t lv_custom_event; // NOLINT extern lv_event_code_t lv_api_event; // NOLINT
extern lv_event_code_t lv_update_event; // NOLINT
#ifdef USE_LVGL_COLOR #ifdef USE_LVGL_COLOR
inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); }
#endif // USE_LVGL_COLOR #endif // USE_LVGL_COLOR
@ -133,6 +134,8 @@ class LvglComponent : public PollingComponent {
void set_paused(bool paused, bool show_snow); void set_paused(bool paused, bool show_snow);
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event); void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event);
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2); void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2);
void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2,
lv_event_code_t event3);
bool is_paused() const { return this->paused_; } bool is_paused() const { return this->paused_; }
void add_page(LvPageType *page); void add_page(LvPageType *page);
void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time); void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time);

View file

@ -3,9 +3,17 @@ from esphome.components import number
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.cpp_generator import MockObj from esphome.cpp_generator import MockObj
from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_UPDATE_ON_RELEASE, CONF_WIDGET
from ..lv_validation import animated from ..lv_validation import animated
from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add from ..lvcode import (
API_EVENT,
EVENT_ARG,
UPDATE_EVENT,
LambdaContext,
LvContext,
lv,
lv_add,
)
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvNumber, lvgl_ns from ..types import LV_EVENT, LvNumber, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets
@ -19,6 +27,7 @@ CONFIG_SCHEMA = (
{ {
cv.Required(CONF_WIDGET): cv.use_id(LvNumber), cv.Required(CONF_WIDGET): cv.use_id(LvNumber),
cv.Optional(CONF_ANIMATED, default=True): animated, cv.Optional(CONF_ANIMATED, default=True): animated,
cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean,
} }
) )
) )
@ -39,14 +48,19 @@ async def to_code(config):
await widget.set_property( await widget.set_property(
"value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED]
) )
lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) lv.event_send(widget.obj, API_EVENT, cg.nullptr)
async with LambdaContext(EVENT_ARG) as event: async with LambdaContext(EVENT_ARG) as event:
event.add(var.publish_state(widget.get_value())) event.add(var.publish_state(widget.get_value()))
event_code = (
LV_EVENT.VALUE_CHANGED
if not config[CONF_UPDATE_ON_RELEASE]
else LV_EVENT.RELEASED
)
async with LvContext(paren): async with LvContext(paren):
lv_add(var.set_control_lambda(await control.get_lambda())) lv_add(var.set_control_lambda(await control.get_lambda()))
lv_add( lv_add(
paren.add_event_cb( paren.add_event_cb(
widget.obj, await event.get_lambda(), LV_EVENT.VALUE_CHANGED widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code
) )
) )
lv_add(var.publish_state(widget.get_value())) lv_add(var.publish_state(widget.get_value()))

View file

@ -20,11 +20,12 @@ class LVGLNumber : public number::Number {
protected: protected:
void control(float value) override { void control(float value) override {
if (this->control_lambda_ != nullptr) if (this->control_lambda_ != nullptr) {
this->control_lambda_(value); this->control_lambda_(value);
else } else {
this->initial_state_ = value; this->initial_state_ = value;
} }
}
std::function<void(float)> control_lambda_{}; std::function<void(float)> control_lambda_{};
optional<float> initial_state_{}; optional<float> initial_state_{};
}; };

View file

@ -103,6 +103,7 @@ STYLE_PROPS = {
).several_of, ).several_of,
"border_width": cv.positive_int, "border_width": cv.positive_int,
"clip_corner": lvalid.lv_bool, "clip_corner": lvalid.lv_bool,
"color_filter_opa": lvalid.opacity,
"height": lvalid.size, "height": lvalid.size,
"image_recolor": lvalid.lv_color, "image_recolor": lvalid.lv_color,
"image_recolor_opa": lvalid.opacity, "image_recolor_opa": lvalid.opacity,

View file

@ -4,7 +4,15 @@ import esphome.config_validation as cv
from esphome.const import CONF_OPTIONS from esphome.const import CONF_OPTIONS
from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add from ..lvcode import (
API_EVENT,
EVENT_ARG,
UPDATE_EVENT,
LambdaContext,
LvContext,
lv,
lv_add,
)
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvSelect, lvgl_ns from ..types import LV_EVENT, LvSelect, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets
@ -33,7 +41,7 @@ async def to_code(config):
pub_ctx.add(selector.publish_index(widget.get_value())) pub_ctx.add(selector.publish_index(widget.get_value()))
async with LambdaContext([(cg.uint16, "v")]) as control: async with LambdaContext([(cg.uint16, "v")]) as control:
await widget.set_property("selected", "v", animated=config[CONF_ANIMATED]) await widget.set_property("selected", "v", animated=config[CONF_ANIMATED])
lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) lv.event_send(widget.obj, API_EVENT, cg.nullptr)
async with LvContext(paren) as ctx: async with LvContext(paren) as ctx:
lv_add(selector.set_control_lambda(await control.get_lambda())) lv_add(selector.set_control_lambda(await control.get_lambda()))
ctx.add( ctx.add(
@ -41,6 +49,7 @@ async def to_code(config):
widget.obj, widget.obj,
await pub_ctx.get_lambda(), await pub_ctx.get_lambda(),
LV_EVENT.VALUE_CHANGED, LV_EVENT.VALUE_CHANGED,
UPDATE_EVENT,
) )
) )
lv_add(selector.publish_index(widget.get_value())) lv_add(selector.publish_index(widget.get_value()))

View file

@ -3,7 +3,15 @@ from esphome.components.sensor import Sensor, new_sensor, sensor_schema
import esphome.config_validation as cv import esphome.config_validation as cv
from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..defines import CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import EVENT_ARG, LVGL_COMP_ARG, LambdaContext, LvContext, lv_add from ..lvcode import (
API_EVENT,
EVENT_ARG,
LVGL_COMP_ARG,
UPDATE_EVENT,
LambdaContext,
LvContext,
lv_add,
)
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvNumber from ..types import LV_EVENT, LvNumber
from ..widgets import Widget, get_widgets from ..widgets import Widget, get_widgets
@ -30,6 +38,10 @@ async def to_code(config):
async with LvContext(paren, LVGL_COMP_ARG): async with LvContext(paren, LVGL_COMP_ARG):
lv_add( lv_add(
paren.add_event_cb( paren.add_event_cb(
widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED widget.obj,
await lamb.get_lambda(),
LV_EVENT.VALUE_CHANGED,
API_EVENT,
UPDATE_EVENT,
) )
) )

View file

@ -5,8 +5,9 @@ from esphome.cpp_generator import MockObj
from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..defines import CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import ( from ..lvcode import (
CUSTOM_EVENT, API_EVENT,
EVENT_ARG, EVENT_ARG,
UPDATE_EVENT,
LambdaContext, LambdaContext,
LvConditional, LvConditional,
LvContext, LvContext,
@ -41,7 +42,7 @@ async def to_code(config):
widget.add_state(LV_STATE.CHECKED) widget.add_state(LV_STATE.CHECKED)
cond.else_() cond.else_()
widget.clear_state(LV_STATE.CHECKED) widget.clear_state(LV_STATE.CHECKED)
lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) lv.event_send(widget.obj, API_EVENT, cg.nullptr)
async with LvContext(paren) as ctx: async with LvContext(paren) as ctx:
lv_add(switch.set_control_lambda(await control.get_lambda())) lv_add(switch.set_control_lambda(await control.get_lambda()))
ctx.add( ctx.add(
@ -49,6 +50,7 @@ async def to_code(config):
widget.obj, widget.obj,
await checked_ctx.get_lambda(), await checked_ctx.get_lambda(),
LV_EVENT.VALUE_CHANGED, LV_EVENT.VALUE_CHANGED,
UPDATE_EVENT,
) )
) )
lv_add(switch.publish_state(widget.get_value())) lv_add(switch.publish_state(widget.get_value()))

View file

@ -20,11 +20,12 @@ class LVGLSwitch : public switch_::Switch {
protected: protected:
void write_state(bool value) override { void write_state(bool value) override {
if (this->state_lambda_ != nullptr) if (this->state_lambda_ != nullptr) {
this->state_lambda_(value); this->state_lambda_(value);
else } else {
this->initial_state_ = value; this->initial_state_ = value;
} }
}
std::function<void(bool)> state_lambda_{}; std::function<void(bool)> state_lambda_{};
optional<bool> initial_state_{}; optional<bool> initial_state_{};
}; };

View file

@ -4,7 +4,15 @@ from esphome.components.text import new_text
import esphome.config_validation as cv import esphome.config_validation as cv
from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..defines import CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add from ..lvcode import (
API_EVENT,
EVENT_ARG,
UPDATE_EVENT,
LambdaContext,
LvContext,
lv,
lv_add,
)
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvText, lvgl_ns from ..types import LV_EVENT, LvText, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets
@ -26,14 +34,17 @@ async def to_code(config):
widget = widget[0] widget = widget[0]
async with LambdaContext([(cg.std_string, "text_value")]) as control: async with LambdaContext([(cg.std_string, "text_value")]) as control:
await widget.set_property("text", "text_value.c_str())") await widget.set_property("text", "text_value.c_str())")
lv.event_send(widget.obj, CUSTOM_EVENT, None) lv.event_send(widget.obj, API_EVENT, None)
async with LambdaContext(EVENT_ARG) as lamb: async with LambdaContext(EVENT_ARG) as lamb:
lv_add(textvar.publish_state(widget.get_value())) lv_add(textvar.publish_state(widget.get_value()))
async with LvContext(paren): async with LvContext(paren):
widget.var.set_control_lambda(await control.get_lambda()) widget.var.set_control_lambda(await control.get_lambda())
lv_add( lv_add(
paren.add_event_cb( paren.add_event_cb(
widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED widget.obj,
await lamb.get_lambda(),
LV_EVENT.VALUE_CHANGED,
UPDATE_EVENT,
) )
) )
lv_add(textvar.publish_state(widget.get_value())) lv_add(textvar.publish_state(widget.get_value()))

View file

@ -20,11 +20,12 @@ class LVGLText : public text::Text {
protected: protected:
void control(const std::string &value) override { void control(const std::string &value) override {
if (this->control_lambda_ != nullptr) if (this->control_lambda_ != nullptr) {
this->control_lambda_(value); this->control_lambda_(value);
else } else {
this->initial_state_ = value; this->initial_state_ = value;
} }
}
std::function<void(const std::string)> control_lambda_{}; std::function<void(const std::string)> control_lambda_{};
optional<std::string> initial_state_{}; optional<std::string> initial_state_{};
}; };

View file

@ -7,7 +7,7 @@ from esphome.components.text_sensor import (
import esphome.config_validation as cv import esphome.config_validation as cv
from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..defines import CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import EVENT_ARG, LambdaContext, LvContext from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvText from ..types import LV_EVENT, LvText
from ..widgets import get_widgets from ..widgets import get_widgets
@ -36,5 +36,7 @@ async def to_code(config):
widget.obj, widget.obj,
await pressed_ctx.get_lambda(), await pressed_ctx.get_lambda(),
LV_EVENT.VALUE_CHANGED, LV_EVENT.VALUE_CHANGED,
API_EVENT,
UPDATE_EVENT,
) )
) )

View file

@ -11,7 +11,15 @@ from .defines import (
LV_EVENT_TRIGGERS, LV_EVENT_TRIGGERS,
literal, literal,
) )
from .lvcode import EVENT_ARG, LambdaContext, LvConditional, lv, lv_add from .lvcode import (
API_EVENT,
EVENT_ARG,
UPDATE_EVENT,
LambdaContext,
LvConditional,
lv,
lv_add,
)
from .types import LV_EVENT from .types import LV_EVENT
from .widgets import widget_map from .widgets import widget_map
@ -34,9 +42,16 @@ async def generate_triggers(lv_component):
conf = conf[0] conf = conf[0]
w.add_flag("LV_OBJ_FLAG_CLICKABLE") w.add_flag("LV_OBJ_FLAG_CLICKABLE")
event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()]) event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()])
await add_trigger(conf, event, lv_component, w) await add_trigger(conf, lv_component, w, event)
for conf in w.config.get(CONF_ON_VALUE, ()): for conf in w.config.get(CONF_ON_VALUE, ()):
await add_trigger(conf, LV_EVENT.VALUE_CHANGED, lv_component, w) await add_trigger(
conf,
lv_component,
w,
LV_EVENT.VALUE_CHANGED,
API_EVENT,
UPDATE_EVENT,
)
# Generate align to directives while we're here # Generate align to directives while we're here
if align_to := w.config.get(CONF_ALIGN_TO): if align_to := w.config.get(CONF_ALIGN_TO):
@ -47,7 +62,7 @@ async def generate_triggers(lv_component):
lv.obj_align_to(w.obj, target, align, x, y) lv.obj_align_to(w.obj, target, align, x, y)
async def add_trigger(conf, event, lv_component, w): async def add_trigger(conf, lv_component, w, *events):
tid = conf[CONF_TRIGGER_ID] tid = conf[CONF_TRIGGER_ID]
trigger = cg.new_Pvariable(tid) trigger = cg.new_Pvariable(tid)
args = w.get_args() args = w.get_args()
@ -56,4 +71,4 @@ async def add_trigger(conf, event, lv_component, w):
async with LambdaContext(EVENT_ARG, where=tid) as context: async with LambdaContext(EVENT_ARG, where=tid) as context:
with LvConditional(w.is_selected()): with LvConditional(w.is_selected()):
lv_add(trigger.trigger(value)) lv_add(trigger.trigger(value))
lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), event)) lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events))

View file

@ -1,39 +1,34 @@
import logging
import json
import hashlib import hashlib
from urllib.parse import urljoin import json
import logging
from pathlib import Path from pathlib import Path
from urllib.parse import urljoin
import requests import requests
import esphome.config_validation as cv from esphome import automation, external_files, git
import esphome.codegen as cg
from esphome.core import CORE, HexInt
from esphome.components import esp32, microphone
from esphome import automation, git, external_files
from esphome.automation import register_action, register_condition from esphome.automation import register_action, register_condition
import esphome.codegen as cg
from esphome.components import esp32, microphone
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
__version__, CONF_FILE,
CONF_ID, CONF_ID,
CONF_MICROPHONE, CONF_MICROPHONE,
CONF_MODEL, CONF_MODEL,
CONF_URL, CONF_PASSWORD,
CONF_FILE,
CONF_PATH, CONF_PATH,
CONF_RAW_DATA_ID,
CONF_REF, CONF_REF,
CONF_REFRESH, CONF_REFRESH,
CONF_TYPE, CONF_TYPE,
CONF_URL,
CONF_USERNAME, CONF_USERNAME,
CONF_PASSWORD,
CONF_RAW_DATA_ID,
TYPE_GIT, TYPE_GIT,
TYPE_LOCAL, TYPE_LOCAL,
__version__,
) )
from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -174,12 +169,12 @@ def _convert_manifest_v1_to_v2(v1_manifest):
CONF_SLIDING_WINDOW_AVERAGE_SIZE CONF_SLIDING_WINDOW_AVERAGE_SIZE
] ]
del v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE] del v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE]
v2_manifest[KEY_MICRO][
CONF_TENSOR_ARENA_SIZE # Original Inception-based V1 manifest models require a minimum of 45672 bytes
] = 45672 # Original Inception-based V1 manifest models require a minimum of 45672 bytes v2_manifest[KEY_MICRO][CONF_TENSOR_ARENA_SIZE] = 45672
v2_manifest[KEY_MICRO][
CONF_FEATURE_STEP_SIZE # Original Inception-based V1 manifest models use a 20 ms feature step size
] = 20 # Original Inception-based V1 manifest models use a 20 ms feature step size v2_manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE] = 20
return v2_manifest return v2_manifest
@ -502,7 +497,7 @@ async def to_code(config):
) )
cg.add(var.set_features_step_size(manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE])) cg.add(var.set_features_step_size(manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE]))
cg.add_library("kahrendt/ESPMicroSpeechFeatures", "1.0.0") cg.add_library("kahrendt/ESPMicroSpeechFeatures", "1.1.0")
MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)}) MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)})

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "esphome/core/defines.h"
#ifdef USE_MQTT
#include <string> #include <string>
#include <map> #include <map>
#include "esphome/components/network/ip_address.h" #include "esphome/components/network/ip_address.h"
@ -67,3 +68,4 @@ class MQTTBackend {
} // namespace mqtt } // namespace mqtt
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,7 +1,9 @@
#include "mqtt_backend_esp32.h"
#ifdef USE_MQTT
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <string> #include <string>
#include "mqtt_backend_esp32.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
@ -189,3 +191,4 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b
} // namespace mqtt } // namespace mqtt
} // namespace esphome } // namespace esphome
#endif // USE_ESP32 #endif // USE_ESP32
#endif

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "mqtt_backend.h"
#ifdef USE_MQTT
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <string> #include <string>
@ -7,7 +9,6 @@
#include <mqtt_client.h> #include <mqtt_client.h>
#include "esphome/components/network/ip_address.h" #include "esphome/components/network/ip_address.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "mqtt_backend.h"
namespace esphome { namespace esphome {
namespace mqtt { namespace mqtt {
@ -174,3 +175,4 @@ class MQTTBackendESP32 final : public MQTTBackend {
} // namespace esphome } // namespace esphome
#endif #endif
#endif

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
#include "mqtt_backend.h"
#ifdef USE_MQTT
#ifdef USE_ESP8266 #ifdef USE_ESP8266
#include "mqtt_backend.h"
#include <AsyncMqttClient.h> #include <AsyncMqttClient.h>
namespace esphome { namespace esphome {
@ -70,3 +71,4 @@ class MQTTBackendESP8266 final : public MQTTBackend {
} // namespace esphome } // namespace esphome
#endif // defined(USE_ESP8266) #endif // defined(USE_ESP8266)
#endif

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
#include "mqtt_backend.h"
#ifdef USE_MQTT
#ifdef USE_LIBRETINY #ifdef USE_LIBRETINY
#include "mqtt_backend.h"
#include <AsyncMqttClient.h> #include <AsyncMqttClient.h>
namespace esphome { namespace esphome {
@ -70,3 +71,4 @@ class MQTTBackendLibreTiny final : public MQTTBackend {
} // namespace esphome } // namespace esphome
#endif // defined(USE_LIBRETINY) #endif // defined(USE_LIBRETINY)
#endif

View file

@ -632,6 +632,7 @@ void MQTTClientComponent::disable_discovery() {
this->discovery_info_ = MQTTDiscoveryInfo{ this->discovery_info_ = MQTTDiscoveryInfo{
.prefix = "", .prefix = "",
.retain = false, .retain = false,
.discover_ip = false,
.clean = false, .clean = false,
.unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR, .unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR,
.object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR, .object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR,

View file

@ -32,6 +32,7 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config): async def to_code(config):
cg.add_define("USE_NETWORK")
if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None: if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None:
cg.add_define("USE_NETWORK_IPV6", enable_ipv6) cg.add_define("USE_NETWORK_IPV6", enable_ipv6)
if enable_ipv6: if enable_ipv6:

View file

@ -1,4 +1,6 @@
#pragma once #pragma once
#include "esphome/core/defines.h"
#ifdef USE_NETWORK
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <cstdio> #include <cstdio>
@ -140,3 +142,4 @@ using IPAddresses = std::array<IPAddress, 5>;
} // namespace network } // namespace network
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,6 +1,6 @@
#include "util.h" #include "util.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#ifdef USE_NETWORK
#ifdef USE_WIFI #ifdef USE_WIFI
#include "esphome/components/wifi/wifi_component.h" #include "esphome/components/wifi/wifi_component.h"
#endif #endif
@ -63,3 +63,4 @@ std::string get_use_address() {
} // namespace network } // namespace network
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "esphome/core/defines.h"
#ifdef USE_NETWORK
#include <string> #include <string>
#include "ip_address.h" #include "ip_address.h"
@ -16,3 +17,4 @@ IPAddresses get_ip_addresses();
} // namespace network } // namespace network
} // namespace esphome } // namespace esphome
#endif

View file

@ -201,9 +201,6 @@ std::string ProntoProtocol::compensate_and_dump_sequence_(const RawTimings &data
out += dump_duration_(t_duration, timebase); out += dump_duration_(t_duration, timebase);
} }
// append minimum gap
out += dump_duration_(PRONTO_DEFAULT_GAP, timebase, true);
return out; return out;
} }

View file

@ -23,10 +23,11 @@ class RGBCTLightOutput : public light::LightOutput {
light::LightTraits get_traits() override { light::LightTraits get_traits() override {
auto traits = light::LightTraits(); auto traits = light::LightTraits();
if (this->color_interlock_) if (this->color_interlock_) {
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE}); traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE});
else } else {
traits.set_supported_color_modes({light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE}); traits.set_supported_color_modes({light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE});
}
traits.set_min_mireds(this->cold_white_temperature_); traits.set_min_mireds(this->cold_white_temperature_);
traits.set_max_mireds(this->warm_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_);
return traits; return traits;

View file

@ -16,10 +16,11 @@ class RGBWLightOutput : public light::LightOutput {
void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; } void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; }
light::LightTraits get_traits() override { light::LightTraits get_traits() override {
auto traits = light::LightTraits(); auto traits = light::LightTraits();
if (this->color_interlock_) if (this->color_interlock_) {
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE}); traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE});
else } else {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE}); traits.set_supported_color_modes({light::ColorMode::RGB_WHITE});
}
return traits; return traits;
} }
void write_state(light::LightState *state) override { void write_state(light::LightState *state) override {

View file

@ -20,10 +20,11 @@ class RGBWWLightOutput : public light::LightOutput {
void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; } void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; }
light::LightTraits get_traits() override { light::LightTraits get_traits() override {
auto traits = light::LightTraits(); auto traits = light::LightTraits();
if (this->color_interlock_) if (this->color_interlock_) {
traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLD_WARM_WHITE}); traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLD_WARM_WHITE});
else } else {
traits.set_supported_color_modes({light::ColorMode::RGB_COLD_WARM_WHITE}); traits.set_supported_color_modes({light::ColorMode::RGB_COLD_WARM_WHITE});
}
traits.set_min_mireds(this->cold_white_temperature_); traits.set_min_mireds(this->cold_white_temperature_);
traits.set_max_mireds(this->warm_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_);
return traits; return traits;

View file

@ -1,13 +1,11 @@
from esphome import automation from esphome import automation
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
from esphome.const import CONF_ID, CONF_DATA import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_DATA, CONF_ID
from esphome.core import CORE from esphome.core import CORE
from esphome.coroutine import coroutine_with_priority from esphome.coroutine import coroutine_with_priority
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@ -22,8 +20,12 @@ PlayAction = speaker_ns.class_(
StopAction = speaker_ns.class_( StopAction = speaker_ns.class_(
"StopAction", automation.Action, cg.Parented.template(Speaker) "StopAction", automation.Action, cg.Parented.template(Speaker)
) )
FinishAction = speaker_ns.class_(
"FinishAction", automation.Action, cg.Parented.template(Speaker)
)
IsPlayingCondition = speaker_ns.class_("IsPlayingCondition", automation.Condition) IsPlayingCondition = speaker_ns.class_("IsPlayingCondition", automation.Condition)
IsStoppedCondition = speaker_ns.class_("IsStoppedCondition", automation.Condition)
async def setup_speaker_core_(var, config): async def setup_speaker_core_(var, config):
@ -75,11 +77,18 @@ async def speaker_play_action(config, action_id, template_arg, args):
automation.register_action("speaker.stop", StopAction, SPEAKER_AUTOMATION_SCHEMA)( automation.register_action("speaker.stop", StopAction, SPEAKER_AUTOMATION_SCHEMA)(
speaker_action speaker_action
) )
automation.register_action("speaker.finish", FinishAction, SPEAKER_AUTOMATION_SCHEMA)(
speaker_action
)
automation.register_condition( automation.register_condition(
"speaker.is_playing", IsPlayingCondition, SPEAKER_AUTOMATION_SCHEMA "speaker.is_playing", IsPlayingCondition, SPEAKER_AUTOMATION_SCHEMA
)(speaker_action) )(speaker_action)
automation.register_condition(
"speaker.is_stopped", IsStoppedCondition, SPEAKER_AUTOMATION_SCHEMA
)(speaker_action)
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
async def to_code(config): async def to_code(config):

View file

@ -39,10 +39,20 @@ template<typename... Ts> class StopAction : public Action<Ts...>, public Parente
void play(Ts... x) override { this->parent_->stop(); } void play(Ts... x) override { this->parent_->stop(); }
}; };
template<typename... Ts> class FinishAction : public Action<Ts...>, public Parented<Speaker> {
public:
void play(Ts... x) override { this->parent_->finish(); }
};
template<typename... Ts> class IsPlayingCondition : public Condition<Ts...>, public Parented<Speaker> { template<typename... Ts> class IsPlayingCondition : public Condition<Ts...>, public Parented<Speaker> {
public: public:
bool check(Ts... x) override { return this->parent_->is_running(); } bool check(Ts... x) override { return this->parent_->is_running(); }
}; };
template<typename... Ts> class IsStoppedCondition : public Condition<Ts...>, public Parented<Speaker> {
public:
bool check(Ts... x) override { return this->parent_->is_stopped(); }
};
} // namespace speaker } // namespace speaker
} // namespace esphome } // namespace esphome

View file

@ -17,10 +17,15 @@ class Speaker {
virtual void start() = 0; virtual void start() = 0;
virtual void stop() = 0; virtual void stop() = 0;
// In compare between *STOP()* and *FINISH()*; *FINISH()* will stop after emptying the play buffer,
// while *STOP()* will break directly.
// When finish() is not implemented on the plateform component it should just do a normal stop.
virtual void finish() { this->stop(); }
virtual bool has_buffered_data() const = 0; virtual bool has_buffered_data() const = 0;
bool is_running() const { return this->state_ == STATE_RUNNING; } bool is_running() const { return this->state_ == STATE_RUNNING; }
bool is_stopped() const { return this->state_ == STATE_STOPPED; }
protected: protected:
State state_{STATE_STOPPED}; State state_{STATE_STOPPED};

View file

@ -46,11 +46,12 @@ class SpiLedStrip : public light::AddressableLight,
void dump_config() override { void dump_config() override {
esph_log_config(TAG, "SPI LED Strip:"); esph_log_config(TAG, "SPI LED Strip:");
esph_log_config(TAG, " LEDs: %d", this->num_leds_); esph_log_config(TAG, " LEDs: %d", this->num_leds_);
if (this->data_rate_ >= spi::DATA_RATE_1MHZ) if (this->data_rate_ >= spi::DATA_RATE_1MHZ) {
esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000)); esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000));
else } else {
esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000)); esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000));
} }
}
void write_state(light::LightState *state) override { void write_state(light::LightState *state) override {
if (this->is_failed()) if (this->is_failed())

View file

@ -1,4 +1,5 @@
#include "wake_on_lan.h" #include "wake_on_lan.h"
#ifdef USE_NETWORK
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/network/ip_address.h" #include "esphome/components/network/ip_address.h"
#include "esphome/components/network/util.h" #include "esphome/components/network/util.h"
@ -85,3 +86,4 @@ void WakeOnLanButton::setup() {
} // namespace wake_on_lan } // namespace wake_on_lan
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "esphome/core/defines.h"
#ifdef USE_NETWORK
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
@ -32,3 +33,4 @@ class WakeOnLanButton : public button::Button, public Component {
} // namespace wake_on_lan } // namespace wake_on_lan
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,4 +1,5 @@
#include "wifi_component.h" #include "wifi_component.h"
#ifdef USE_WIFI
#include <cinttypes> #include <cinttypes>
#include <map> #include <map>
@ -856,3 +857,4 @@ WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-con
} // namespace wifi } // namespace wifi
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,9 +1,10 @@
#pragma once #pragma once
#include "esphome/core/defines.h"
#ifdef USE_WIFI
#include "esphome/components/network/ip_address.h" #include "esphome/components/network/ip_address.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include <string> #include <string>
@ -442,3 +443,4 @@ template<typename... Ts> class WiFiDisableAction : public Action<Ts...> {
} // namespace wifi } // namespace wifi
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,5 +1,6 @@
#include "wifi_component.h" #include "wifi_component.h"
#ifdef USE_WIFI
#ifdef USE_ESP32_FRAMEWORK_ARDUINO #ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include <esp_netif.h> #include <esp_netif.h>
@ -802,3 +803,4 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddr
} // namespace esphome } // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO #endif // USE_ESP32_FRAMEWORK_ARDUINO
#endif

View file

@ -1,6 +1,7 @@
#include "wifi_component.h" #include "wifi_component.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#ifdef USE_WIFI
#ifdef USE_ESP8266 #ifdef USE_ESP8266
#include <user_interface.h> #include <user_interface.h>
@ -834,3 +835,4 @@ void WiFiComponent::wifi_loop_() {}
} // namespace esphome } // namespace esphome
#endif #endif
#endif

View file

@ -1,5 +1,6 @@
#include "wifi_component.h" #include "wifi_component.h"
#ifdef USE_WIFI
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#include <esp_event.h> #include <esp_event.h>
@ -1010,3 +1011,4 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) {
} // namespace esphome } // namespace esphome
#endif // USE_ESP_IDF #endif // USE_ESP_IDF
#endif

View file

@ -1,5 +1,6 @@
#include "wifi_component.h" #include "wifi_component.h"
#ifdef USE_WIFI
#ifdef USE_LIBRETINY #ifdef USE_LIBRETINY
#include <utility> #include <utility>
@ -468,3 +469,4 @@ void WiFiComponent::wifi_loop_() {}
} // namespace esphome } // namespace esphome
#endif // USE_LIBRETINY #endif // USE_LIBRETINY
#endif

View file

@ -1,6 +1,7 @@
#include "wifi_component.h" #include "wifi_component.h"
#ifdef USE_WIFI
#ifdef USE_RP2040 #ifdef USE_RP2040
#include "lwip/dns.h" #include "lwip/dns.h"
@ -218,3 +219,4 @@ void WiFiComponent::wifi_pre_setup_() {}
} // namespace esphome } // namespace esphome
#endif #endif
#endif

View file

@ -1,4 +1,5 @@
#include "wifi_info_text_sensor.h" #include "wifi_info_text_sensor.h"
#ifdef USE_WIFI
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome {
@ -15,3 +16,4 @@ void DNSAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo DNS Addre
} // namespace wifi_info } // namespace wifi_info
} // namespace esphome } // namespace esphome
#endif

View file

@ -3,6 +3,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/wifi/wifi_component.h" #include "esphome/components/wifi/wifi_component.h"
#ifdef USE_WIFI
#include <array> #include <array>
namespace esphome { namespace esphome {
@ -131,3 +132,4 @@ class MacAddressWifiInfo : public Component, public text_sensor::TextSensor {
} // namespace wifi_info } // namespace wifi_info
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,4 +1,5 @@
#include "wifi_signal_sensor.h" #include "wifi_signal_sensor.h"
#ifdef USE_WIFI
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome {
@ -10,3 +11,4 @@ void WiFiSignalSensor::dump_config() { LOG_SENSOR("", "WiFi Signal", this); }
} // namespace wifi_signal } // namespace wifi_signal
} // namespace esphome } // namespace esphome
#endif

View file

@ -4,7 +4,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/wifi/wifi_component.h" #include "esphome/components/wifi/wifi_component.h"
#ifdef USE_WIFI
namespace esphome { namespace esphome {
namespace wifi_signal { namespace wifi_signal {
@ -19,3 +19,4 @@ class WiFiSignalSensor : public sensor::Sensor, public PollingComponent {
} // namespace wifi_signal } // namespace wifi_signal
} // namespace esphome } // namespace esphome
#endif

View file

@ -1,30 +1,30 @@
"""Constants used by esphome.""" """Constants used by esphome."""
__version__ = "2024.8.0-dev" __version__ = "2024.9.0-dev"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = ( VALID_SUBSTITUTIONS_CHARACTERS = (
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
) )
PLATFORM_BK72XX = "bk72xx"
PLATFORM_ESP32 = "esp32" PLATFORM_ESP32 = "esp32"
PLATFORM_ESP8266 = "esp8266" PLATFORM_ESP8266 = "esp8266"
PLATFORM_RP2040 = "rp2040"
PLATFORM_HOST = "host" PLATFORM_HOST = "host"
PLATFORM_BK72XX = "bk72xx"
PLATFORM_RTL87XX = "rtl87xx"
PLATFORM_LIBRETINY_OLDSTYLE = "libretiny" PLATFORM_LIBRETINY_OLDSTYLE = "libretiny"
PLATFORM_NRF52 = "nrf52" PLATFORM_NRF52 = "nrf52"
PLATFORM_RP2040 = "rp2040"
PLATFORM_RTL87XX = "rtl87xx"
TARGET_PLATFORMS = [ TARGET_PLATFORMS = [
PLATFORM_BK72XX,
PLATFORM_ESP32, PLATFORM_ESP32,
PLATFORM_ESP8266, PLATFORM_ESP8266,
PLATFORM_RP2040,
PLATFORM_HOST, PLATFORM_HOST,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_LIBRETINY_OLDSTYLE, PLATFORM_LIBRETINY_OLDSTYLE,
PLATFORM_NRF52, PLATFORM_NRF52,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
] ]
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
@ -97,6 +97,7 @@ CONF_BOARD_FLASH_MODE = "board_flash_mode"
CONF_BORDER = "border" CONF_BORDER = "border"
CONF_BRANCH = "branch" CONF_BRANCH = "branch"
CONF_BRIGHTNESS = "brightness" CONF_BRIGHTNESS = "brightness"
CONF_BRIGHTNESS_LIMITS = "brightness_limits"
CONF_BROKER = "broker" CONF_BROKER = "broker"
CONF_BSSID = "bssid" CONF_BSSID = "bssid"
CONF_BUFFER_SIZE = "buffer_size" CONF_BUFFER_SIZE = "buffer_size"
@ -431,6 +432,7 @@ CONF_LIGHT = "light"
CONF_LIGHT_ID = "light_id" CONF_LIGHT_ID = "light_id"
CONF_LIGHTNING_ENERGY = "lightning_energy" CONF_LIGHTNING_ENERGY = "lightning_energy"
CONF_LIGHTNING_THRESHOLD = "lightning_threshold" CONF_LIGHTNING_THRESHOLD = "lightning_threshold"
CONF_LIMIT_MODE = "limit_mode"
CONF_LINE_THICKNESS = "line_thickness" CONF_LINE_THICKNESS = "line_thickness"
CONF_LINE_TYPE = "line_type" CONF_LINE_TYPE = "line_type"
CONF_LOADED_INTEGRATIONS = "loaded_integrations" CONF_LOADED_INTEGRATIONS = "loaded_integrations"

View file

@ -246,162 +246,180 @@ class Application {
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
const std::vector<binary_sensor::BinarySensor *> &get_binary_sensors() { return this->binary_sensors_; } const std::vector<binary_sensor::BinarySensor *> &get_binary_sensors() { return this->binary_sensors_; }
binary_sensor::BinarySensor *get_binary_sensor_by_key(uint32_t key, bool include_internal = false) { binary_sensor::BinarySensor *get_binary_sensor_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->binary_sensors_) for (auto *obj : this->binary_sensors_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
const std::vector<switch_::Switch *> &get_switches() { return this->switches_; } const std::vector<switch_::Switch *> &get_switches() { return this->switches_; }
switch_::Switch *get_switch_by_key(uint32_t key, bool include_internal = false) { switch_::Switch *get_switch_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->switches_) for (auto *obj : this->switches_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
const std::vector<button::Button *> &get_buttons() { return this->buttons_; } const std::vector<button::Button *> &get_buttons() { return this->buttons_; }
button::Button *get_button_by_key(uint32_t key, bool include_internal = false) { button::Button *get_button_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->buttons_) for (auto *obj : this->buttons_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
const std::vector<sensor::Sensor *> &get_sensors() { return this->sensors_; } const std::vector<sensor::Sensor *> &get_sensors() { return this->sensors_; }
sensor::Sensor *get_sensor_by_key(uint32_t key, bool include_internal = false) { sensor::Sensor *get_sensor_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->sensors_) for (auto *obj : this->sensors_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
const std::vector<text_sensor::TextSensor *> &get_text_sensors() { return this->text_sensors_; } const std::vector<text_sensor::TextSensor *> &get_text_sensors() { return this->text_sensors_; }
text_sensor::TextSensor *get_text_sensor_by_key(uint32_t key, bool include_internal = false) { text_sensor::TextSensor *get_text_sensor_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->text_sensors_) for (auto *obj : this->text_sensors_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
const std::vector<fan::Fan *> &get_fans() { return this->fans_; } const std::vector<fan::Fan *> &get_fans() { return this->fans_; }
fan::Fan *get_fan_by_key(uint32_t key, bool include_internal = false) { fan::Fan *get_fan_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->fans_) for (auto *obj : this->fans_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_COVER #ifdef USE_COVER
const std::vector<cover::Cover *> &get_covers() { return this->covers_; } const std::vector<cover::Cover *> &get_covers() { return this->covers_; }
cover::Cover *get_cover_by_key(uint32_t key, bool include_internal = false) { cover::Cover *get_cover_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->covers_) for (auto *obj : this->covers_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
const std::vector<light::LightState *> &get_lights() { return this->lights_; } const std::vector<light::LightState *> &get_lights() { return this->lights_; }
light::LightState *get_light_by_key(uint32_t key, bool include_internal = false) { light::LightState *get_light_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->lights_) for (auto *obj : this->lights_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
const std::vector<climate::Climate *> &get_climates() { return this->climates_; } const std::vector<climate::Climate *> &get_climates() { return this->climates_; }
climate::Climate *get_climate_by_key(uint32_t key, bool include_internal = false) { climate::Climate *get_climate_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->climates_) for (auto *obj : this->climates_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_NUMBER #ifdef USE_NUMBER
const std::vector<number::Number *> &get_numbers() { return this->numbers_; } const std::vector<number::Number *> &get_numbers() { return this->numbers_; }
number::Number *get_number_by_key(uint32_t key, bool include_internal = false) { number::Number *get_number_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->numbers_) for (auto *obj : this->numbers_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_DATETIME_DATE #ifdef USE_DATETIME_DATE
const std::vector<datetime::DateEntity *> &get_dates() { return this->dates_; } const std::vector<datetime::DateEntity *> &get_dates() { return this->dates_; }
datetime::DateEntity *get_date_by_key(uint32_t key, bool include_internal = false) { datetime::DateEntity *get_date_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->dates_) for (auto *obj : this->dates_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_DATETIME_TIME #ifdef USE_DATETIME_TIME
const std::vector<datetime::TimeEntity *> &get_times() { return this->times_; } const std::vector<datetime::TimeEntity *> &get_times() { return this->times_; }
datetime::TimeEntity *get_time_by_key(uint32_t key, bool include_internal = false) { datetime::TimeEntity *get_time_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->times_) for (auto *obj : this->times_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_DATETIME_DATETIME #ifdef USE_DATETIME_DATETIME
const std::vector<datetime::DateTimeEntity *> &get_datetimes() { return this->datetimes_; } const std::vector<datetime::DateTimeEntity *> &get_datetimes() { return this->datetimes_; }
datetime::DateTimeEntity *get_datetime_by_key(uint32_t key, bool include_internal = false) { datetime::DateTimeEntity *get_datetime_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->datetimes_) for (auto *obj : this->datetimes_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_TEXT #ifdef USE_TEXT
const std::vector<text::Text *> &get_texts() { return this->texts_; } const std::vector<text::Text *> &get_texts() { return this->texts_; }
text::Text *get_text_by_key(uint32_t key, bool include_internal = false) { text::Text *get_text_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->texts_) for (auto *obj : this->texts_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
const std::vector<select::Select *> &get_selects() { return this->selects_; } const std::vector<select::Select *> &get_selects() { return this->selects_; }
select::Select *get_select_by_key(uint32_t key, bool include_internal = false) { select::Select *get_select_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->selects_) for (auto *obj : this->selects_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_LOCK #ifdef USE_LOCK
const std::vector<lock::Lock *> &get_locks() { return this->locks_; } const std::vector<lock::Lock *> &get_locks() { return this->locks_; }
lock::Lock *get_lock_by_key(uint32_t key, bool include_internal = false) { lock::Lock *get_lock_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->locks_) for (auto *obj : this->locks_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_VALVE #ifdef USE_VALVE
const std::vector<valve::Valve *> &get_valves() { return this->valves_; } const std::vector<valve::Valve *> &get_valves() { return this->valves_; }
valve::Valve *get_valve_by_key(uint32_t key, bool include_internal = false) { valve::Valve *get_valve_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->valves_) for (auto *obj : this->valves_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
const std::vector<media_player::MediaPlayer *> &get_media_players() { return this->media_players_; } const std::vector<media_player::MediaPlayer *> &get_media_players() { return this->media_players_; }
media_player::MediaPlayer *get_media_player_by_key(uint32_t key, bool include_internal = false) { media_player::MediaPlayer *get_media_player_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->media_players_) for (auto *obj : this->media_players_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
@ -411,9 +429,10 @@ class Application {
return this->alarm_control_panels_; return this->alarm_control_panels_;
} }
alarm_control_panel::AlarmControlPanel *get_alarm_control_panel_by_key(uint32_t key, bool include_internal = false) { alarm_control_panel::AlarmControlPanel *get_alarm_control_panel_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->alarm_control_panels_) for (auto *obj : this->alarm_control_panels_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
@ -421,9 +440,10 @@ class Application {
#ifdef USE_EVENT #ifdef USE_EVENT
const std::vector<event::Event *> &get_events() { return this->events_; } const std::vector<event::Event *> &get_events() { return this->events_; }
event::Event *get_event_by_key(uint32_t key, bool include_internal = false) { event::Event *get_event_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->events_) for (auto *obj : this->events_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif
@ -431,9 +451,10 @@ class Application {
#ifdef USE_UPDATE #ifdef USE_UPDATE
const std::vector<update::UpdateEntity *> &get_updates() { return this->updates_; } const std::vector<update::UpdateEntity *> &get_updates() { return this->updates_; }
update::UpdateEntity *get_update_by_key(uint32_t key, bool include_internal = false) { update::UpdateEntity *get_update_by_key(uint32_t key, bool include_internal = false) {
for (auto *obj : this->updates_) for (auto *obj : this->updates_) {
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
return obj; return obj;
}
return nullptr; return nullptr;
} }
#endif #endif

View file

@ -82,7 +82,7 @@ template<typename... Ts> class Condition {
} }
protected: protected:
template<int... S> bool check_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) { template<int... S> bool check_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
return this->check(std::get<S>(tuple)...); return this->check(std::get<S>(tuple)...);
} }
}; };
@ -156,7 +156,7 @@ template<typename... Ts> class Action {
} }
} }
} }
template<int... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) { template<int... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
this->play_next_(std::get<S>(tuple)...); this->play_next_(std::get<S>(tuple)...);
} }
void play_next_tuple_(const std::tuple<Ts...> &tuple) { void play_next_tuple_(const std::tuple<Ts...> &tuple) {
@ -223,7 +223,9 @@ template<typename... Ts> class ActionList {
} }
protected: protected:
template<int... S> void play_tuple_(const std::tuple<Ts...> &tuple, seq<S...>) { this->play(std::get<S>(tuple)...); } template<int... S> void play_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
this->play(std::get<S>(tuple)...);
}
Action<Ts...> *actions_begin_{nullptr}; Action<Ts...> *actions_begin_{nullptr};
Action<Ts...> *actions_end_{nullptr}; Action<Ts...> *actions_end_{nullptr};

View file

@ -278,10 +278,11 @@ template<typename... Ts> class RepeatAction : public Action<Ts...> {
this->then_.add_actions(actions); this->then_.add_actions(actions);
this->then_.add_action(new LambdaAction<uint32_t, Ts...>([this](uint32_t iteration, Ts... x) { this->then_.add_action(new LambdaAction<uint32_t, Ts...>([this](uint32_t iteration, Ts... x) {
iteration++; iteration++;
if (iteration >= this->count_.value(x...)) if (iteration >= this->count_.value(x...)) {
this->play_next_tuple_(this->var_); this->play_next_tuple_(this->var_);
else } else {
this->then_.play(iteration, x...); this->then_.play(iteration, x...);
}
})); }));
} }

134
esphome/core/bytebuffer.cpp Normal file
View file

@ -0,0 +1,134 @@
#include "bytebuffer.h"
#include <cassert>
namespace esphome {
ByteBuffer ByteBuffer::create(size_t capacity) {
std::vector<uint8_t> data(capacity);
return {data};
}
ByteBuffer ByteBuffer::wrap(uint8_t *ptr, size_t len) {
std::vector<uint8_t> data(ptr, ptr + len);
return {data};
}
ByteBuffer ByteBuffer::wrap(std::vector<uint8_t> data) { return {std::move(data)}; }
void ByteBuffer::set_limit(size_t limit) {
assert(limit <= this->get_capacity());
this->limit_ = limit;
}
void ByteBuffer::set_position(size_t position) {
assert(position <= this->get_limit());
this->position_ = position;
}
void ByteBuffer::clear() {
this->limit_ = this->get_capacity();
this->position_ = 0;
}
uint16_t ByteBuffer::get_uint16() {
assert(this->get_remaining() >= 2);
uint16_t value;
if (endianness_ == LITTLE) {
value = this->data_[this->position_++];
value |= this->data_[this->position_++] << 8;
} else {
value = this->data_[this->position_++] << 8;
value |= this->data_[this->position_++];
}
return value;
}
uint32_t ByteBuffer::get_uint32() {
assert(this->get_remaining() >= 4);
uint32_t value;
if (endianness_ == LITTLE) {
value = this->data_[this->position_++];
value |= this->data_[this->position_++] << 8;
value |= this->data_[this->position_++] << 16;
value |= this->data_[this->position_++] << 24;
} else {
value = this->data_[this->position_++] << 24;
value |= this->data_[this->position_++] << 16;
value |= this->data_[this->position_++] << 8;
value |= this->data_[this->position_++];
}
return value;
}
uint32_t ByteBuffer::get_uint24() {
assert(this->get_remaining() >= 3);
uint32_t value;
if (endianness_ == LITTLE) {
value = this->data_[this->position_++];
value |= this->data_[this->position_++] << 8;
value |= this->data_[this->position_++] << 16;
} else {
value = this->data_[this->position_++] << 16;
value |= this->data_[this->position_++] << 8;
value |= this->data_[this->position_++];
}
return value;
}
uint32_t ByteBuffer::get_int24() {
auto value = this->get_uint24();
uint32_t mask = (~(uint32_t) 0) << 23;
if ((value & mask) != 0)
value |= mask;
return value;
}
uint8_t ByteBuffer::get_uint8() {
assert(this->get_remaining() >= 1);
return this->data_[this->position_++];
}
float ByteBuffer::get_float() {
auto value = this->get_uint32();
return *(float *) &value;
}
void ByteBuffer::put_uint8(uint8_t value) {
assert(this->get_remaining() >= 1);
this->data_[this->position_++] = value;
}
void ByteBuffer::put_uint16(uint16_t value) {
assert(this->get_remaining() >= 2);
if (this->endianness_ == LITTLE) {
this->data_[this->position_++] = (uint8_t) value;
this->data_[this->position_++] = (uint8_t) (value >> 8);
} else {
this->data_[this->position_++] = (uint8_t) (value >> 8);
this->data_[this->position_++] = (uint8_t) value;
}
}
void ByteBuffer::put_uint24(uint32_t value) {
assert(this->get_remaining() >= 3);
if (this->endianness_ == LITTLE) {
this->data_[this->position_++] = (uint8_t) value;
this->data_[this->position_++] = (uint8_t) (value >> 8);
this->data_[this->position_++] = (uint8_t) (value >> 16);
} else {
this->data_[this->position_++] = (uint8_t) (value >> 16);
this->data_[this->position_++] = (uint8_t) (value >> 8);
this->data_[this->position_++] = (uint8_t) value;
}
}
void ByteBuffer::put_uint32(uint32_t value) {
assert(this->get_remaining() >= 4);
if (this->endianness_ == LITTLE) {
this->data_[this->position_++] = (uint8_t) value;
this->data_[this->position_++] = (uint8_t) (value >> 8);
this->data_[this->position_++] = (uint8_t) (value >> 16);
this->data_[this->position_++] = (uint8_t) (value >> 24);
} else {
this->data_[this->position_++] = (uint8_t) (value >> 24);
this->data_[this->position_++] = (uint8_t) (value >> 16);
this->data_[this->position_++] = (uint8_t) (value >> 8);
this->data_[this->position_++] = (uint8_t) value;
}
}
void ByteBuffer::put_float(float value) { this->put_uint32(*(uint32_t *) &value); }
void ByteBuffer::flip() {
this->limit_ = this->position_;
this->position_ = 0;
}
} // namespace esphome

96
esphome/core/bytebuffer.h Normal file
View file

@ -0,0 +1,96 @@
#pragma once
#include <utility>
#include <vector>
#include <cinttypes>
#include <cstddef>
namespace esphome {
enum Endian { LITTLE, BIG };
/**
* A class modelled on the Java ByteBuffer class. It wraps a vector of bytes and permits putting and getting
* items of various sizes, with an automatically incremented position.
*
* There are three variables maintained pointing into the buffer:
*
* 0 <= position <= limit <= capacity
*
* capacity: the maximum amount of data that can be stored
* limit: the limit of the data currently available to get or put
* position: the current insert or extract position
*
* In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore
* the position to the mark.
*
* The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order.
*
*/
class ByteBuffer {
public:
/**
* Create a new Bytebuffer with the given capacity
*/
static ByteBuffer create(size_t capacity);
/**
* Wrap an existing vector in a Bytebufffer
*/
static ByteBuffer wrap(std::vector<uint8_t> data);
/**
* Wrap an existing array in a Bytebufffer
*/
static ByteBuffer wrap(uint8_t *ptr, size_t len);
// Get one byte from the buffer, increment position by 1
uint8_t get_uint8();
// Get a 16 bit unsigned value, increment by 2
uint16_t get_uint16();
// Get a 24 bit unsigned value, increment by 3
uint32_t get_uint24();
// Get a 32 bit unsigned value, increment by 4
uint32_t get_uint32();
// signed versions of the get functions
uint8_t get_int8() { return (int8_t) this->get_uint8(); };
int16_t get_int16() { return (int16_t) this->get_uint16(); }
uint32_t get_int24();
int32_t get_int32() { return (int32_t) this->get_uint32(); }
// Get a float value, increment by 4
float get_float();
// put values into the buffer, increment the position accordingly
void put_uint8(uint8_t value);
void put_uint16(uint16_t value);
void put_uint24(uint32_t value);
void put_uint32(uint32_t value);
void put_float(float value);
inline size_t get_capacity() const { return this->data_.size(); }
inline size_t get_position() const { return this->position_; }
inline size_t get_limit() const { return this->limit_; }
inline size_t get_remaining() const { return this->get_limit() - this->get_position(); }
inline Endian get_endianness() const { return this->endianness_; }
inline void mark() { this->mark_ = this->position_; }
inline void big_endian() { this->endianness_ = BIG; }
inline void little_endian() { this->endianness_ = LITTLE; }
void set_limit(size_t limit);
void set_position(size_t position);
// set position to 0, limit to capacity.
void clear();
// set limit to current position, postition to zero. Used when swapping from write to read operations.
void flip();
// retrieve a pointer to the underlying data.
uint8_t *array() { return this->data_.data(); };
void rewind() { this->position_ = 0; }
void reset() { this->position_ = this->mark_; }
protected:
ByteBuffer(std::vector<uint8_t> data) : data_(std::move(data)) { this->limit_ = this->get_capacity(); }
std::vector<uint8_t> data_;
Endian endianness_{LITTLE};
size_t position_{0};
size_t mark_{0};
size_t limit_{0};
};
} // namespace esphome

View file

@ -85,22 +85,26 @@ struct Color {
} }
inline Color operator+(const Color &add) const ESPHOME_ALWAYS_INLINE { inline Color operator+(const Color &add) const ESPHOME_ALWAYS_INLINE {
Color ret; Color ret;
if (uint8_t(add.r + this->r) < this->r) if (uint8_t(add.r + this->r) < this->r) {
ret.r = 255; ret.r = 255;
else } else {
ret.r = this->r + add.r; ret.r = this->r + add.r;
if (uint8_t(add.g + this->g) < this->g) }
if (uint8_t(add.g + this->g) < this->g) {
ret.g = 255; ret.g = 255;
else } else {
ret.g = this->g + add.g; ret.g = this->g + add.g;
if (uint8_t(add.b + this->b) < this->b) }
if (uint8_t(add.b + this->b) < this->b) {
ret.b = 255; ret.b = 255;
else } else {
ret.b = this->b + add.b; ret.b = this->b + add.b;
if (uint8_t(add.w + this->w) < this->w) }
if (uint8_t(add.w + this->w) < this->w) {
ret.w = 255; ret.w = 255;
else } else {
ret.w = this->w + add.w; ret.w = this->w + add.w;
}
return ret; return ret;
} }
inline Color &operator+=(const Color &add) ESPHOME_ALWAYS_INLINE { return *this = (*this) + add; } inline Color &operator+=(const Color &add) ESPHOME_ALWAYS_INLINE { return *this = (*this) + add; }
@ -108,22 +112,26 @@ struct Color {
inline Color &operator+=(uint8_t add) ESPHOME_ALWAYS_INLINE { return *this = (*this) + add; } inline Color &operator+=(uint8_t add) ESPHOME_ALWAYS_INLINE { return *this = (*this) + add; }
inline Color operator-(const Color &subtract) const ESPHOME_ALWAYS_INLINE { inline Color operator-(const Color &subtract) const ESPHOME_ALWAYS_INLINE {
Color ret; Color ret;
if (subtract.r > this->r) if (subtract.r > this->r) {
ret.r = 0; ret.r = 0;
else } else {
ret.r = this->r - subtract.r; ret.r = this->r - subtract.r;
if (subtract.g > this->g) }
if (subtract.g > this->g) {
ret.g = 0; ret.g = 0;
else } else {
ret.g = this->g - subtract.g; ret.g = this->g - subtract.g;
if (subtract.b > this->b) }
if (subtract.b > this->b) {
ret.b = 0; ret.b = 0;
else } else {
ret.b = this->b - subtract.b; ret.b = this->b - subtract.b;
if (subtract.w > this->w) }
if (subtract.w > this->w) {
ret.w = 0; ret.w = 0;
else } else {
ret.w = this->w - subtract.w; ret.w = this->w - subtract.w;
}
return ret; return ret;
} }
inline Color &operator-=(const Color &subtract) ESPHOME_ALWAYS_INLINE { return *this = (*this) - subtract; } inline Color &operator-=(const Color &subtract) ESPHOME_ALWAYS_INLINE { return *this = (*this) - subtract; }

View file

@ -51,6 +51,7 @@
#define USE_MDNS #define USE_MDNS
#define USE_MEDIA_PLAYER #define USE_MEDIA_PLAYER
#define USE_MQTT #define USE_MQTT
#define USE_NETWORK
#define USE_NEXTION_TFT_UPLOAD #define USE_NEXTION_TFT_UPLOAD
#define USE_NUMBER #define USE_NUMBER
#define USE_ONLINE_IMAGE_PNG_SUPPORT #define USE_ONLINE_IMAGE_PNG_SUPPORT

View file

@ -24,7 +24,7 @@ namespace esphome {
struct nullopt_t { // NOLINT struct nullopt_t { // NOLINT
struct init {}; // NOLINT struct init {}; // NOLINT
nullopt_t(init) {} nullopt_t(init /*unused*/) {}
}; };
// extra parenthesis to prevent the most vexing parse: // extra parenthesis to prevent the most vexing parse:
@ -42,13 +42,13 @@ template<typename T> class optional { // NOLINT
optional() {} optional() {}
optional(nullopt_t) {} optional(nullopt_t /*unused*/) {}
optional(T const &arg) : has_value_(true), value_(arg) {} // NOLINT optional(T const &arg) : has_value_(true), value_(arg) {} // NOLINT
template<class U> optional(optional<U> const &other) : has_value_(other.has_value()), value_(other.value()) {} template<class U> optional(optional<U> const &other) : has_value_(other.has_value()), value_(other.value()) {}
optional &operator=(nullopt_t) { optional &operator=(nullopt_t /*unused*/) {
reset(); reset();
return *this; return *this;
} }
@ -104,7 +104,6 @@ template<typename T> class optional { // NOLINT
has_value_ = true; has_value_ = true;
} }
private:
bool has_value_{false}; // NOLINT bool has_value_{false}; // NOLINT
value_type value_; // NOLINT value_type value_; // NOLINT
}; };
@ -131,29 +130,29 @@ template<typename T, typename U> inline bool operator>=(optional<T> const &x, op
// Comparison with nullopt // Comparison with nullopt
template<typename T> inline bool operator==(optional<T> const &x, nullopt_t) { return (!x); } template<typename T> inline bool operator==(optional<T> const &x, nullopt_t /*unused*/) { return (!x); }
template<typename T> inline bool operator==(nullopt_t, optional<T> const &x) { return (!x); } template<typename T> inline bool operator==(nullopt_t /*unused*/, optional<T> const &x) { return (!x); }
template<typename T> inline bool operator!=(optional<T> const &x, nullopt_t) { return bool(x); } template<typename T> inline bool operator!=(optional<T> const &x, nullopt_t /*unused*/) { return bool(x); }
template<typename T> inline bool operator!=(nullopt_t, optional<T> const &x) { return bool(x); } template<typename T> inline bool operator!=(nullopt_t /*unused*/, optional<T> const &x) { return bool(x); }
template<typename T> inline bool operator<(optional<T> const &, nullopt_t) { return false; } template<typename T> inline bool operator<(optional<T> const & /*unused*/, nullopt_t /*unused*/) { return false; }
template<typename T> inline bool operator<(nullopt_t, optional<T> const &x) { return bool(x); } template<typename T> inline bool operator<(nullopt_t /*unused*/, optional<T> const &x) { return bool(x); }
template<typename T> inline bool operator<=(optional<T> const &x, nullopt_t) { return (!x); } template<typename T> inline bool operator<=(optional<T> const &x, nullopt_t /*unused*/) { return (!x); }
template<typename T> inline bool operator<=(nullopt_t, optional<T> const &) { return true; } template<typename T> inline bool operator<=(nullopt_t /*unused*/, optional<T> const & /*unused*/) { return true; }
template<typename T> inline bool operator>(optional<T> const &x, nullopt_t) { return bool(x); } template<typename T> inline bool operator>(optional<T> const &x, nullopt_t /*unused*/) { return bool(x); }
template<typename T> inline bool operator>(nullopt_t, optional<T> const &) { return false; } template<typename T> inline bool operator>(nullopt_t /*unused*/, optional<T> const & /*unused*/) { return false; }
template<typename T> inline bool operator>=(optional<T> const &, nullopt_t) { return true; } template<typename T> inline bool operator>=(optional<T> const & /*unused*/, nullopt_t /*unused*/) { return true; }
template<typename T> inline bool operator>=(nullopt_t, optional<T> const &x) { return (!x); } template<typename T> inline bool operator>=(nullopt_t /*unused*/, optional<T> const &x) { return (!x); }
// Comparison with T // Comparison with T

View file

@ -145,7 +145,7 @@ framework = espidf
lib_deps = lib_deps =
${common:idf.lib_deps} ${common:idf.lib_deps}
droscy/esp_wireguard@0.4.2 ; wireguard droscy/esp_wireguard@0.4.2 ; wireguard
kahrendt/ESPMicroSpeechFeatures@1.0.0 ; micro_wake_word kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word
build_flags = build_flags =
${common:idf.build_flags} ${common:idf.build_flags}
-Wno-nonnull-compare -Wno-nonnull-compare

View file

@ -7,6 +7,7 @@ spi:
sensor: sensor:
- platform: atm90e32 - platform: atm90e32
cs_pin: 13 cs_pin: 13
id: chip1
phase_a: phase_a:
voltage: voltage:
name: EMON Line Voltage A name: EMON Line Voltage A
@ -49,3 +50,11 @@ sensor:
line_frequency: 60Hz line_frequency: 60Hz
current_phases: 3 current_phases: 3
gain_pga: 2X gain_pga: 2X
enable_offset_calibration: True
button:
- platform: atm90e32
id: chip1
run_offset_calibration:
name: "Chip1 - Run Offset Calibration"
clear_offset_calibration:
name: "Chip1 - Clear Offset Calibration"

View file

@ -7,6 +7,7 @@ spi:
sensor: sensor:
- platform: atm90e32 - platform: atm90e32
cs_pin: 8 cs_pin: 8
id: chip1
phase_a: phase_a:
voltage: voltage:
name: EMON Line Voltage A name: EMON Line Voltage A
@ -49,3 +50,11 @@ sensor:
line_frequency: 60Hz line_frequency: 60Hz
current_phases: 3 current_phases: 3
gain_pga: 2X gain_pga: 2X
enable_offset_calibration: True
button:
- platform: atm90e32
id: chip1
run_offset_calibration:
name: "Chip1 - Run Offset Calibration"
clear_offset_calibration:
name: "Chip1 - Clear Offset Calibration"

View file

@ -7,6 +7,7 @@ spi:
sensor: sensor:
- platform: atm90e32 - platform: atm90e32
cs_pin: 8 cs_pin: 8
id: chip1
phase_a: phase_a:
voltage: voltage:
name: EMON Line Voltage A name: EMON Line Voltage A
@ -49,3 +50,11 @@ sensor:
line_frequency: 60Hz line_frequency: 60Hz
current_phases: 3 current_phases: 3
gain_pga: 2X gain_pga: 2X
enable_offset_calibration: True
button:
- platform: atm90e32
id: chip1
run_offset_calibration:
name: "Chip1 - Run Offset Calibration"
clear_offset_calibration:
name: "Chip1 - Clear Offset Calibration"

View file

@ -7,6 +7,7 @@ spi:
sensor: sensor:
- platform: atm90e32 - platform: atm90e32
cs_pin: 13 cs_pin: 13
id: chip1
phase_a: phase_a:
voltage: voltage:
name: EMON Line Voltage A name: EMON Line Voltage A
@ -49,3 +50,11 @@ sensor:
line_frequency: 60Hz line_frequency: 60Hz
current_phases: 3 current_phases: 3
gain_pga: 2X gain_pga: 2X
enable_offset_calibration: True
button:
- platform: atm90e32
id: chip1
run_offset_calibration:
name: "Chip1 - Run Offset Calibration"
clear_offset_calibration:
name: "Chip1 - Clear Offset Calibration"

View file

@ -7,6 +7,7 @@ spi:
sensor: sensor:
- platform: atm90e32 - platform: atm90e32
cs_pin: 5 cs_pin: 5
id: chip1
phase_a: phase_a:
voltage: voltage:
name: EMON Line Voltage A name: EMON Line Voltage A
@ -49,3 +50,42 @@ sensor:
line_frequency: 60Hz line_frequency: 60Hz
current_phases: 3 current_phases: 3
gain_pga: 2X gain_pga: 2X
enable_offset_calibration: True
- platform: atm90e32
cs_pin: 4
id: chip2
phase_a:
voltage:
name: EMON Line Voltage A
current:
name: EMON CT1 Current
power:
name: EMON Active Power CT1
reactive_power:
name: EMON Reactive Power CT1
power_factor:
name: EMON Power Factor CT1
gain_voltage: 7305
gain_ct: 27961
phase_c:
voltage:
name: EMON Line Voltage C
current:
name: EMON CT2 Current
power:
name: EMON Active Power CT2
reactive_power:
name: EMON Reactive Power CT2
power_factor:
name: EMON Power Factor CT2
gain_voltage: 7305
gain_ct: 27961
line_frequency: 60Hz
current_phases: 2
button:
- platform: atm90e32
id: chip1
run_offset_calibration:
name: "Chip1 - Run Offset Calibration"
clear_offset_calibration:
name: "Chip1 - Clear Offset Calibration"

View file

@ -7,6 +7,7 @@ spi:
sensor: sensor:
- platform: atm90e32 - platform: atm90e32
cs_pin: 5 cs_pin: 5
id: chip1
phase_a: phase_a:
voltage: voltage:
name: EMON Line Voltage A name: EMON Line Voltage A
@ -49,3 +50,11 @@ sensor:
line_frequency: 60Hz line_frequency: 60Hz
current_phases: 3 current_phases: 3
gain_pga: 2X gain_pga: 2X
enable_offset_calibration: True
button:
- platform: atm90e32
id: chip1
run_offset_calibration:
name: "Chip1 - Run Offset Calibration"
clear_offset_calibration:
name: "Chip1 - Clear Offset Calibration"

View file

@ -32,6 +32,11 @@ wifi:
api: api:
switch:
- platform: homeassistant
entity_id: switch.my_cool_switch
id: my_cool_switch
binary_sensor: binary_sensor:
- platform: homeassistant - platform: homeassistant
entity_id: binary_sensor.hello_world entity_id: binary_sensor.hello_world
@ -41,6 +46,11 @@ binary_sensor:
attribute: world attribute: world
id: ha_hello_world_binary_attribute id: ha_hello_world_binary_attribute
number:
- platform: homeassistant
entity_id: number.hello_world
id: ha_hello_world_number
sensor: sensor:
- platform: homeassistant - platform: homeassistant
entity_id: sensor.hello_world entity_id: sensor.hello_world

View file

@ -15,6 +15,8 @@ esphome:
- light.dim_relative: - light.dim_relative:
id: test_monochromatic_light id: test_monochromatic_light
relative_brightness: 5% relative_brightness: 5%
brightness_limits:
max_brightness: 90%
output: output:
- platform: gpio - platform: gpio

View file

@ -15,6 +15,8 @@ esphome:
- light.dim_relative: - light.dim_relative:
id: test_monochromatic_light id: test_monochromatic_light
relative_brightness: 5% relative_brightness: 5%
brightness_limits:
max_brightness: 90%
output: output:
- platform: gpio - platform: gpio

View file

@ -15,6 +15,8 @@ esphome:
- light.dim_relative: - light.dim_relative:
id: test_monochromatic_light id: test_monochromatic_light
relative_brightness: 5% relative_brightness: 5%
brightness_limits:
max_brightness: 90%
output: output:
- platform: gpio - platform: gpio

View file

@ -15,6 +15,8 @@ esphome:
- light.dim_relative: - light.dim_relative:
id: test_monochromatic_light id: test_monochromatic_light
relative_brightness: 5% relative_brightness: 5%
brightness_limits:
max_brightness: 90%
output: output:
- platform: gpio - platform: gpio

View file

@ -15,6 +15,8 @@ esphome:
- light.dim_relative: - light.dim_relative:
id: test_monochromatic_light id: test_monochromatic_light
relative_brightness: 5% relative_brightness: 5%
brightness_limits:
max_brightness: 90%
output: output:
- platform: gpio - platform: gpio

Some files were not shown because too many files have changed in this diff Show more