mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
Merge branch 'dev' into mcuboot_support
This commit is contained in:
commit
da2a8f64e2
83 changed files with 1819 additions and 241 deletions
2
.github/workflows/sync-device-classes.yml
vendored
2
.github/workflows/sync-device-classes.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
||||||
python ./script/sync-device_class.py
|
python ./script/sync-device_class.py
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@v7.0.3
|
uses: peter-evans/create-pull-request@v7.0.5
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@nabucasa.com>
|
committer: esphomebot <esphome@nabucasa.com>
|
||||||
|
|
|
@ -86,7 +86,7 @@ esphome/components/cap1188/* @mreditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
esphome/components/ccs811/* @habbie
|
esphome/components/ccs811/* @habbie
|
||||||
esphome/components/cd74hc4067/* @asoehlke
|
esphome/components/cd74hc4067/* @asoehlke
|
||||||
esphome/components/ch422g/* @jesterret
|
esphome/components/ch422g/* @clydebarrow @jesterret
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
esphome/components/color_temperature/* @jesserockz
|
esphome/components/color_temperature/* @jesserockz
|
||||||
|
@ -166,6 +166,7 @@ esphome/components/haier/* @paveldn
|
||||||
esphome/components/haier/binary_sensor/* @paveldn
|
esphome/components/haier/binary_sensor/* @paveldn
|
||||||
esphome/components/haier/button/* @paveldn
|
esphome/components/haier/button/* @paveldn
|
||||||
esphome/components/haier/sensor/* @paveldn
|
esphome/components/haier/sensor/* @paveldn
|
||||||
|
esphome/components/haier/switch/* @paveldn
|
||||||
esphome/components/haier/text_sensor/* @paveldn
|
esphome/components/haier/text_sensor/* @paveldn
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
|
@ -397,6 +398,7 @@ esphome/components/sun_gtil2/* @Mat931
|
||||||
esphome/components/switch/* @esphome/core
|
esphome/components/switch/* @esphome/core
|
||||||
esphome/components/t6615/* @tylermenezes
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tca9548a/* @andreashergert1984
|
esphome/components/tca9548a/* @andreashergert1984
|
||||||
|
esphome/components/tca9555/* @mobrembski
|
||||||
esphome/components/tcl112/* @glmnet
|
esphome/components/tcl112/* @glmnet
|
||||||
esphome/components/tee501/* @Stock-M
|
esphome/components/tee501/* @Stock-M
|
||||||
esphome/components/teleinfo/* @0hax
|
esphome/components/teleinfo/* @0hax
|
||||||
|
|
|
@ -1242,6 +1242,9 @@ VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configura
|
||||||
}
|
}
|
||||||
resp.available_wake_words.push_back(std::move(resp_wake_word));
|
resp.available_wake_words.push_back(std::move(resp_wake_word));
|
||||||
}
|
}
|
||||||
|
for (auto &wake_word_id : config.active_wake_words) {
|
||||||
|
resp.active_wake_words.push_back(wake_word_id);
|
||||||
|
}
|
||||||
resp.max_active_wake_words = config.max_active_wake_words;
|
resp.max_active_wake_words = config.max_active_wake_words;
|
||||||
}
|
}
|
||||||
return resp;
|
return resp;
|
||||||
|
|
|
@ -145,8 +145,9 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def reset_energy_to_code(config, action_id, template_arg, args):
|
async def reset_energy_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
|
from esphome.components.i2c import I2CBus
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_I2C_ID,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_INPUT,
|
CONF_INPUT,
|
||||||
CONF_INVERTED,
|
CONF_INVERTED,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
CONF_NUMBER,
|
CONF_NUMBER,
|
||||||
|
CONF_OPEN_DRAIN,
|
||||||
CONF_OUTPUT,
|
CONF_OUTPUT,
|
||||||
CONF_RESTORE_VALUE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@jesterret"]
|
CODEOWNERS = ["@jesterret", "@clydebarrow"]
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
ch422g_ns = cg.esphome_ns.namespace("ch422g")
|
ch422g_ns = cg.esphome_ns.namespace("ch422g")
|
||||||
|
@ -23,29 +25,36 @@ CH422GGPIOPin = ch422g_ns.class_(
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_CH422G = "ch422g"
|
CONF_CH422G = "ch422g"
|
||||||
CONFIG_SCHEMA = (
|
|
||||||
cv.Schema(
|
# Note that no address is configurable - each register in the CH422G has a dedicated i2c address
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.declare_id(CH422GComponent),
|
cv.GenerateID(CONF_ID): cv.declare_id(CH422GComponent),
|
||||||
cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
|
cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CBus),
|
||||||
}
|
}
|
||||||
)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
.extend(i2c.i2c_device_schema(0x24))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
# Can't use register_i2c_device because there is no CONF_ADDRESS
|
||||||
|
parent = await cg.get_variable(config[CONF_I2C_ID])
|
||||||
|
cg.add(var.set_i2c_bus(parent))
|
||||||
|
|
||||||
|
|
||||||
|
# This is used as a final validation step so that modes have been fully transformed.
|
||||||
|
def pin_mode_check(pin_config, _):
|
||||||
|
if pin_config[CONF_MODE][CONF_INPUT] and pin_config[CONF_NUMBER] >= 8:
|
||||||
|
raise cv.Invalid("CH422G only supports input on pins 0-7")
|
||||||
|
if pin_config[CONF_MODE][CONF_OPEN_DRAIN] and pin_config[CONF_NUMBER] < 8:
|
||||||
|
raise cv.Invalid("CH422G only supports open drain output on pins 8-11")
|
||||||
|
|
||||||
|
|
||||||
CH422G_PIN_SCHEMA = pins.gpio_base_schema(
|
CH422G_PIN_SCHEMA = pins.gpio_base_schema(
|
||||||
CH422GGPIOPin,
|
CH422GGPIOPin,
|
||||||
cv.int_range(min=0, max=7),
|
cv.int_range(min=0, max=11),
|
||||||
modes=[CONF_INPUT, CONF_OUTPUT],
|
modes=[CONF_INPUT, CONF_OUTPUT, CONF_OPEN_DRAIN],
|
||||||
).extend(
|
).extend(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_CH422G): cv.use_id(CH422GComponent),
|
cv.Required(CONF_CH422G): cv.use_id(CH422GComponent),
|
||||||
|
@ -53,7 +62,7 @@ CH422G_PIN_SCHEMA = pins.gpio_base_schema(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA)
|
@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA, pin_mode_check)
|
||||||
async def ch422g_pin_to_code(config):
|
async def ch422g_pin_to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
parent = await cg.get_variable(config[CONF_CH422G])
|
parent = await cg.get_variable(config[CONF_CH422G])
|
||||||
|
|
|
@ -4,33 +4,33 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ch422g {
|
namespace ch422g {
|
||||||
|
|
||||||
const uint8_t CH422G_REG_IN = 0x26;
|
static const uint8_t CH422G_REG_MODE = 0x24;
|
||||||
const uint8_t CH422G_REG_OUT = 0x38;
|
static const uint8_t CH422G_MODE_OUTPUT = 0x01; // enables output mode on 0-7
|
||||||
const uint8_t OUT_REG_DEFAULT_VAL = 0xdf;
|
static const uint8_t CH422G_MODE_OPEN_DRAIN = 0x04; // enables open drain mode on 8-11
|
||||||
|
static const uint8_t CH422G_REG_IN = 0x26; // read reg for input bits
|
||||||
|
static const uint8_t CH422G_REG_OUT = 0x38; // write reg for output bits 0-7
|
||||||
|
static const uint8_t CH422G_REG_OUT_UPPER = 0x23; // write reg for output bits 8-11
|
||||||
|
|
||||||
static const char *const TAG = "ch422g";
|
static const char *const TAG = "ch422g";
|
||||||
|
|
||||||
void CH422GComponent::setup() {
|
void CH422GComponent::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up CH422G...");
|
ESP_LOGCONFIG(TAG, "Setting up CH422G...");
|
||||||
// Test to see if device exists
|
// set outputs before mode
|
||||||
if (!this->read_inputs_()) {
|
this->write_outputs_();
|
||||||
|
// Set mode and check for errors
|
||||||
|
if (!this->set_mode_(this->mode_value_) || !this->read_inputs_()) {
|
||||||
ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_);
|
ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore defaults over whatever got saved on last boot
|
ESP_LOGCONFIG(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
|
||||||
if (!this->restore_value_) {
|
|
||||||
this->write_output_(OUT_REG_DEFAULT_VAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
|
|
||||||
this->status_has_error());
|
this->status_has_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CH422GComponent::loop() {
|
void CH422GComponent::loop() {
|
||||||
// Clear all the previously read flags.
|
// Clear all the previously read flags.
|
||||||
this->pin_read_cache_ = 0x00;
|
this->pin_read_flags_ = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CH422GComponent::dump_config() {
|
void CH422GComponent::dump_config() {
|
||||||
|
@ -41,82 +41,99 @@ void CH422GComponent::dump_config() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ch422g doesn't have any flag support (needs docs?)
|
void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||||
void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {}
|
if (pin < 8) {
|
||||||
|
if (flags & gpio::FLAG_OUTPUT) {
|
||||||
|
this->mode_value_ |= CH422G_MODE_OUTPUT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (flags & gpio::FLAG_OPEN_DRAIN) {
|
||||||
|
this->mode_value_ |= CH422G_MODE_OPEN_DRAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CH422GComponent::digital_read(uint8_t pin) {
|
bool CH422GComponent::digital_read(uint8_t pin) {
|
||||||
if (this->pin_read_cache_ == 0 || this->pin_read_cache_ & (1 << pin)) {
|
if (this->pin_read_flags_ == 0 || this->pin_read_flags_ & (1 << pin)) {
|
||||||
// Read values on first access or in case it's being read again in the same loop
|
// Read values on first access or in case it's being read again in the same loop
|
||||||
this->read_inputs_();
|
this->read_inputs_();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->pin_read_cache_ |= (1 << pin);
|
this->pin_read_flags_ |= (1 << pin);
|
||||||
return this->state_mask_ & (1 << pin);
|
return (this->input_bits_ & (1 << pin)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CH422GComponent::digital_write(uint8_t pin, bool value) {
|
void CH422GComponent::digital_write(uint8_t pin, bool value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this->write_output_(this->state_mask_ | (1 << pin));
|
this->output_bits_ |= (1 << pin);
|
||||||
} else {
|
} else {
|
||||||
this->write_output_(this->state_mask_ & ~(1 << pin));
|
this->output_bits_ &= ~(1 << pin);
|
||||||
}
|
}
|
||||||
|
this->write_outputs_();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CH422GComponent::read_inputs_() {
|
bool CH422GComponent::read_inputs_() {
|
||||||
if (this->is_failed()) {
|
if (this->is_failed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
uint8_t result;
|
||||||
uint8_t temp = 0;
|
// reading inputs requires the chip to be in input mode, possibly temporarily.
|
||||||
if ((this->last_error_ = this->read(&temp, 1)) != esphome::i2c::ERROR_OK) {
|
if (this->mode_value_ & CH422G_MODE_OUTPUT) {
|
||||||
this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str());
|
this->set_mode_(this->mode_value_ & ~CH422G_MODE_OUTPUT);
|
||||||
return false;
|
result = this->read_reg_(CH422G_REG_IN);
|
||||||
|
this->set_mode_(this->mode_value_);
|
||||||
|
} else {
|
||||||
|
result = this->read_reg_(CH422G_REG_IN);
|
||||||
}
|
}
|
||||||
|
this->input_bits_ = result;
|
||||||
uint8_t output = 0;
|
|
||||||
if ((this->last_error_ = this->bus_->read(CH422G_REG_IN, &output, 1)) != esphome::i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->state_mask_ = output;
|
|
||||||
this->status_clear_warning();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CH422GComponent::write_output_(uint8_t value) {
|
|
||||||
const uint8_t temp = 1;
|
|
||||||
if ((this->last_error_ = this->write(&temp, 1, false)) != esphome::i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning(str_sprintf("write_output_(): I2C I/O error: %d", (int) this->last_error_).c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t write_mask = value;
|
|
||||||
if ((this->last_error_ = this->bus_->write(CH422G_REG_OUT, &write_mask, 1)) != esphome::i2c::ERROR_OK) {
|
|
||||||
this->status_set_warning(
|
|
||||||
str_sprintf("write_output_(): I2C I/O error: %d for write_mask: %d", (int) this->last_error_, (int) write_mask)
|
|
||||||
.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->state_mask_ = value;
|
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address.
|
||||||
|
bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) {
|
||||||
|
auto err = this->bus_->write(reg, &value, 1);
|
||||||
|
if (err != i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CH422GComponent::read_reg_(uint8_t reg) {
|
||||||
|
uint8_t value;
|
||||||
|
auto err = this->bus_->read(reg, &value, 1);
|
||||||
|
if (err != i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
this->status_clear_warning();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CH422GComponent::set_mode_(uint8_t mode) { return this->write_reg_(CH422G_REG_MODE, mode); }
|
||||||
|
|
||||||
|
bool CH422GComponent::write_outputs_() {
|
||||||
|
return this->write_reg_(CH422G_REG_OUT, static_cast<uint8_t>(this->output_bits_)) &&
|
||||||
|
this->write_reg_(CH422G_REG_OUT_UPPER, static_cast<uint8_t>(this->output_bits_ >> 8));
|
||||||
|
}
|
||||||
|
|
||||||
float CH422GComponent::get_setup_priority() const { return setup_priority::IO; }
|
float CH422GComponent::get_setup_priority() const { return setup_priority::IO; }
|
||||||
|
|
||||||
// Run our loop() method very early in the loop, so that we cache read values
|
// Run our loop() method very early in the loop, so that we cache read values
|
||||||
// before other components call our digital_read() method.
|
// before other components call our digital_read() method.
|
||||||
float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
||||||
|
|
||||||
void CH422GGPIOPin::setup() { pin_mode(flags_); }
|
|
||||||
void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||||
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; }
|
||||||
|
|
||||||
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); }
|
||||||
std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); }
|
std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); }
|
||||||
|
void CH422GGPIOPin::set_flags(gpio::Flags flags) {
|
||||||
|
flags_ = flags;
|
||||||
|
this->parent_->pin_mode(this->pin_, flags);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ch422g
|
} // namespace ch422g
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -23,32 +23,30 @@ class CH422GComponent : public Component, public i2c::I2CDevice {
|
||||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
float get_loop_priority() const override;
|
float get_loop_priority() const override;
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool write_reg_(uint8_t reg, uint8_t value);
|
||||||
|
uint8_t read_reg_(uint8_t reg);
|
||||||
|
bool set_mode_(uint8_t mode);
|
||||||
bool read_inputs_();
|
bool read_inputs_();
|
||||||
|
bool write_outputs_();
|
||||||
bool write_output_(uint8_t value);
|
|
||||||
|
|
||||||
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||||
uint8_t state_mask_{0x00};
|
uint16_t output_bits_{0x00};
|
||||||
/// Flags to check if read previously during this loop
|
/// Flags to check if read previously during this loop
|
||||||
uint8_t pin_read_cache_ = {0x00};
|
uint8_t pin_read_flags_ = {0x00};
|
||||||
/// Storage for last I2C error seen
|
/// Copy of last read values
|
||||||
esphome::i2c::ErrorCode last_error_;
|
uint8_t input_bits_ = {0x00};
|
||||||
/// Whether we want to override stored values on expander
|
/// Copy of the mode value
|
||||||
bool restore_value_{false};
|
uint8_t mode_value_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper class to expose a CH422G pin as an internal input GPIO pin.
|
/// Helper class to expose a CH422G pin as a GPIO pin.
|
||||||
class CH422GGPIOPin : public GPIOPin {
|
class CH422GGPIOPin : public GPIOPin {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override{};
|
||||||
void pin_mode(gpio::Flags flags) override;
|
void pin_mode(gpio::Flags flags) override;
|
||||||
bool digital_read() override;
|
bool digital_read() override;
|
||||||
void digital_write(bool value) override;
|
void digital_write(bool value) override;
|
||||||
|
@ -57,13 +55,13 @@ class CH422GGPIOPin : public GPIOPin {
|
||||||
void set_parent(CH422GComponent *parent) { parent_ = parent; }
|
void set_parent(CH422GComponent *parent) { parent_ = parent; }
|
||||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||||
void set_flags(gpio::Flags flags) { flags_ = flags; }
|
void set_flags(gpio::Flags flags);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CH422GComponent *parent_;
|
CH422GComponent *parent_{};
|
||||||
uint8_t pin_;
|
uint8_t pin_{};
|
||||||
bool inverted_;
|
bool inverted_{};
|
||||||
gpio::Flags flags_;
|
gpio::Flags flags_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ch422g
|
} // namespace ch422g
|
||||||
|
|
|
@ -147,6 +147,7 @@ void CSE7766Component::parse_data_() {
|
||||||
float power = 0.0f;
|
float power = 0.0f;
|
||||||
if (power_cycle_exceeds_range) {
|
if (power_cycle_exceeds_range) {
|
||||||
// Datasheet: power cycle exceeding range means active power is 0
|
// Datasheet: power cycle exceeding range means active power is 0
|
||||||
|
have_power = true;
|
||||||
if (this->power_sensor_ != nullptr) {
|
if (this->power_sensor_ != nullptr) {
|
||||||
this->power_sensor_->publish_state(0.0f);
|
this->power_sensor_->publish_state(0.0f);
|
||||||
}
|
}
|
||||||
|
@ -178,6 +179,15 @@ void CSE7766Component::parse_data_() {
|
||||||
if (this->apparent_power_sensor_ != nullptr) {
|
if (this->apparent_power_sensor_ != nullptr) {
|
||||||
this->apparent_power_sensor_->publish_state(apparent_power);
|
this->apparent_power_sensor_->publish_state(apparent_power);
|
||||||
}
|
}
|
||||||
|
if (have_power && this->reactive_power_sensor_ != nullptr) {
|
||||||
|
const float reactive_power = apparent_power - power;
|
||||||
|
if (reactive_power < 0.0f) {
|
||||||
|
ESP_LOGD(TAG, "Impossible reactive power: %.4f is negative", reactive_power);
|
||||||
|
this->reactive_power_sensor_->publish_state(0.0f);
|
||||||
|
} else {
|
||||||
|
this->reactive_power_sensor_->publish_state(reactive_power);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this->power_factor_sensor_ != nullptr && (have_power || power_cycle_exceeds_range)) {
|
if (this->power_factor_sensor_ != nullptr && (have_power || power_cycle_exceeds_range)) {
|
||||||
float pf = NAN;
|
float pf = NAN;
|
||||||
if (apparent_power > 0) {
|
if (apparent_power > 0) {
|
||||||
|
@ -232,6 +242,7 @@ void CSE7766Component::dump_config() {
|
||||||
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||||
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
|
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
|
||||||
LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_);
|
LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Reactive Power", this->reactive_power_sensor_);
|
||||||
LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_);
|
LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_);
|
||||||
this->check_uart_settings(4800);
|
this->check_uart_settings(4800);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
||||||
void set_apparent_power_sensor(sensor::Sensor *apparent_power_sensor) {
|
void set_apparent_power_sensor(sensor::Sensor *apparent_power_sensor) {
|
||||||
apparent_power_sensor_ = apparent_power_sensor;
|
apparent_power_sensor_ = apparent_power_sensor;
|
||||||
}
|
}
|
||||||
|
void set_reactive_power_sensor(sensor::Sensor *reactive_power_sensor) {
|
||||||
|
reactive_power_sensor_ = reactive_power_sensor;
|
||||||
|
}
|
||||||
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
|
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
|
||||||
|
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
@ -35,6 +38,7 @@ class CSE7766Component : public Component, public uart::UARTDevice {
|
||||||
sensor::Sensor *power_sensor_{nullptr};
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
sensor::Sensor *energy_sensor_{nullptr};
|
sensor::Sensor *energy_sensor_{nullptr};
|
||||||
sensor::Sensor *apparent_power_sensor_{nullptr};
|
sensor::Sensor *apparent_power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *reactive_power_sensor_{nullptr};
|
||||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||||
uint32_t cf_pulses_total_{0};
|
uint32_t cf_pulses_total_{0};
|
||||||
uint16_t cf_pulses_last_{0};
|
uint16_t cf_pulses_last_{0};
|
||||||
|
|
|
@ -8,18 +8,21 @@ from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_POWER,
|
CONF_POWER,
|
||||||
CONF_POWER_FACTOR,
|
CONF_POWER_FACTOR,
|
||||||
|
CONF_REACTIVE_POWER,
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
DEVICE_CLASS_APPARENT_POWER,
|
DEVICE_CLASS_APPARENT_POWER,
|
||||||
DEVICE_CLASS_CURRENT,
|
DEVICE_CLASS_CURRENT,
|
||||||
DEVICE_CLASS_ENERGY,
|
DEVICE_CLASS_ENERGY,
|
||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_POWER,
|
||||||
DEVICE_CLASS_POWER_FACTOR,
|
DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
DEVICE_CLASS_REACTIVE_POWER,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
UNIT_AMPERE,
|
UNIT_AMPERE,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
UNIT_VOLT_AMPS,
|
UNIT_VOLT_AMPS,
|
||||||
|
UNIT_VOLT_AMPS_REACTIVE,
|
||||||
UNIT_WATT,
|
UNIT_WATT,
|
||||||
UNIT_WATT_HOURS,
|
UNIT_WATT_HOURS,
|
||||||
)
|
)
|
||||||
|
@ -62,6 +65,12 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
device_class=DEVICE_CLASS_APPARENT_POWER,
|
device_class=DEVICE_CLASS_APPARENT_POWER,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_REACTIVE_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
device_class=DEVICE_CLASS_POWER_FACTOR,
|
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
@ -94,6 +103,9 @@ async def to_code(config):
|
||||||
if apparent_power_config := config.get(CONF_APPARENT_POWER):
|
if apparent_power_config := config.get(CONF_APPARENT_POWER):
|
||||||
sens = await sensor.new_sensor(apparent_power_config)
|
sens = await sensor.new_sensor(apparent_power_config)
|
||||||
cg.add(var.set_apparent_power_sensor(sens))
|
cg.add(var.set_apparent_power_sensor(sens))
|
||||||
|
if reactive_power_config := config.get(CONF_REACTIVE_POWER):
|
||||||
|
sens = await sensor.new_sensor(reactive_power_config)
|
||||||
|
cg.add(var.set_reactive_power_sensor(sens))
|
||||||
if power_factor_config := config.get(CONF_POWER_FACTOR):
|
if power_factor_config := config.get(CONF_POWER_FACTOR):
|
||||||
sens = await sensor.new_sensor(power_factor_config)
|
sens = await sensor.new_sensor(power_factor_config)
|
||||||
cg.add(var.set_power_factor_sensor(sens))
|
cg.add(var.set_power_factor_sensor(sens))
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import binary_sensor, esp32_ble_server, output
|
from esphome.components import binary_sensor, esp32_ble_server, output
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID
|
||||||
|
|
||||||
AUTO_LOAD = ["esp32_ble_server"]
|
AUTO_LOAD = ["esp32_ble_server"]
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
|
@ -11,13 +12,36 @@ CONF_AUTHORIZED_DURATION = "authorized_duration"
|
||||||
CONF_AUTHORIZER = "authorizer"
|
CONF_AUTHORIZER = "authorizer"
|
||||||
CONF_BLE_SERVER_ID = "ble_server_id"
|
CONF_BLE_SERVER_ID = "ble_server_id"
|
||||||
CONF_IDENTIFY_DURATION = "identify_duration"
|
CONF_IDENTIFY_DURATION = "identify_duration"
|
||||||
|
CONF_ON_PROVISIONED = "on_provisioned"
|
||||||
|
CONF_ON_PROVISIONING = "on_provisioning"
|
||||||
|
CONF_ON_START = "on_start"
|
||||||
|
CONF_ON_STOP = "on_stop"
|
||||||
CONF_STATUS_INDICATOR = "status_indicator"
|
CONF_STATUS_INDICATOR = "status_indicator"
|
||||||
CONF_WIFI_TIMEOUT = "wifi_timeout"
|
CONF_WIFI_TIMEOUT = "wifi_timeout"
|
||||||
|
|
||||||
|
improv_ns = cg.esphome_ns.namespace("improv")
|
||||||
|
Error = improv_ns.enum("Error")
|
||||||
|
State = improv_ns.enum("State")
|
||||||
|
|
||||||
esp32_improv_ns = cg.esphome_ns.namespace("esp32_improv")
|
esp32_improv_ns = cg.esphome_ns.namespace("esp32_improv")
|
||||||
ESP32ImprovComponent = esp32_improv_ns.class_(
|
ESP32ImprovComponent = esp32_improv_ns.class_(
|
||||||
"ESP32ImprovComponent", cg.Component, esp32_ble_server.BLEServiceComponent
|
"ESP32ImprovComponent", cg.Component, esp32_ble_server.BLEServiceComponent
|
||||||
)
|
)
|
||||||
|
ESP32ImprovProvisionedTrigger = esp32_improv_ns.class_(
|
||||||
|
"ESP32ImprovProvisionedTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ESP32ImprovProvisioningTrigger = esp32_improv_ns.class_(
|
||||||
|
"ESP32ImprovProvisioningTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ESP32ImprovStartTrigger = esp32_improv_ns.class_(
|
||||||
|
"ESP32ImprovStartTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ESP32ImprovStateTrigger = esp32_improv_ns.class_(
|
||||||
|
"ESP32ImprovStateTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ESP32ImprovStoppedTrigger = esp32_improv_ns.class_(
|
||||||
|
"ESP32ImprovStoppedTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
@ -37,6 +61,37 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_WIFI_TIMEOUT, default="1min"
|
CONF_WIFI_TIMEOUT, default="1min"
|
||||||
): cv.positive_time_period_milliseconds,
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
ESP32ImprovProvisionedTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_PROVISIONING): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
ESP32ImprovProvisioningTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_START): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStartTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStateTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_STOP): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
ESP32ImprovStoppedTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
@ -63,3 +118,29 @@ async def to_code(config):
|
||||||
if CONF_STATUS_INDICATOR in config:
|
if CONF_STATUS_INDICATOR in config:
|
||||||
status_indicator = await cg.get_variable(config[CONF_STATUS_INDICATOR])
|
status_indicator = await cg.get_variable(config[CONF_STATUS_INDICATOR])
|
||||||
cg.add(var.set_status_indicator(status_indicator))
|
cg.add(var.set_status_indicator(status_indicator))
|
||||||
|
|
||||||
|
use_state_callback = False
|
||||||
|
for conf in config.get(CONF_ON_PROVISIONED, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_PROVISIONING, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_START, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_STATE, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(
|
||||||
|
trigger, [(State, "state"), (Error, "error")], conf
|
||||||
|
)
|
||||||
|
use_state_callback = True
|
||||||
|
for conf in config.get(CONF_ON_STOP, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
use_state_callback = True
|
||||||
|
if use_state_callback:
|
||||||
|
cg.add_define("USE_ESP32_IMPROV_STATE_CALLBACK")
|
||||||
|
|
72
esphome/components/esp32_improv/automation.h
Normal file
72
esphome/components/esp32_improv/automation.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
#include "esp32_improv_component.h"
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
|
||||||
|
#include <improv.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace esp32_improv {
|
||||||
|
|
||||||
|
class ESP32ImprovProvisionedTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ESP32ImprovProvisionedTrigger(ESP32ImprovComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
|
||||||
|
if (state == improv::STATE_PROVISIONED && !parent->is_failed()) {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESP32ImprovProvisioningTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ESP32ImprovProvisioningTrigger(ESP32ImprovComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
|
||||||
|
if (state == improv::STATE_PROVISIONING && !parent->is_failed()) {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESP32ImprovStartTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ESP32ImprovStartTrigger(ESP32ImprovComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
|
||||||
|
if ((state == improv::STATE_AUTHORIZED || state == improv::STATE_AWAITING_AUTHORIZATION) &&
|
||||||
|
!parent->is_failed()) {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESP32ImprovStateTrigger : public Trigger<improv::State, improv::Error> {
|
||||||
|
public:
|
||||||
|
explicit ESP32ImprovStateTrigger(ESP32ImprovComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
|
||||||
|
if (!parent->is_failed()) {
|
||||||
|
trigger(state, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ESP32ImprovStoppedTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ESP32ImprovStoppedTrigger(ESP32ImprovComponent *parent) {
|
||||||
|
parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) {
|
||||||
|
if (state == improv::STATE_STOPPED && !parent->is_failed()) {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esp32_improv
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -68,7 +68,12 @@ void ESP32ImprovComponent::setup_characteristics() {
|
||||||
|
|
||||||
void ESP32ImprovComponent::loop() {
|
void ESP32ImprovComponent::loop() {
|
||||||
if (!global_ble_server->is_running()) {
|
if (!global_ble_server->is_running()) {
|
||||||
|
if (this->state_ != improv::STATE_STOPPED) {
|
||||||
this->state_ = improv::STATE_STOPPED;
|
this->state_ = improv::STATE_STOPPED;
|
||||||
|
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
this->state_callback_.call(this->state_, this->error_state_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
this->incoming_data_.clear();
|
this->incoming_data_.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -217,6 +222,9 @@ void ESP32ImprovComponent::set_state_(improv::State state) {
|
||||||
service_data[7] = 0x00; // Reserved
|
service_data[7] = 0x00; // Reserved
|
||||||
|
|
||||||
esp32_ble::global_ble->advertising_set_service_data(service_data);
|
esp32_ble::global_ble->advertising_set_service_data(service_data);
|
||||||
|
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
this->state_callback_.call(this->state_, this->error_state_);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32ImprovComponent::set_error_(improv::Error error) {
|
void ESP32ImprovComponent::set_error_(improv::Error error) {
|
||||||
|
@ -270,7 +278,7 @@ void ESP32ImprovComponent::dump_config() {
|
||||||
void ESP32ImprovComponent::process_incoming_data_() {
|
void ESP32ImprovComponent::process_incoming_data_() {
|
||||||
uint8_t length = this->incoming_data_[1];
|
uint8_t length = this->incoming_data_[1];
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
|
ESP_LOGV(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
|
||||||
if (this->incoming_data_.size() - 3 == length) {
|
if (this->incoming_data_.size() - 3 == length) {
|
||||||
this->set_error_(improv::ERROR_NONE);
|
this->set_error_(improv::ERROR_NONE);
|
||||||
improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);
|
improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);
|
||||||
|
@ -295,7 +303,7 @@ void ESP32ImprovComponent::process_incoming_data_() {
|
||||||
wifi::global_wifi_component->set_sta(sta);
|
wifi::global_wifi_component->set_sta(sta);
|
||||||
wifi::global_wifi_component->start_connecting(sta, false);
|
wifi::global_wifi_component->start_connecting(sta, false);
|
||||||
this->set_state_(improv::STATE_PROVISIONING);
|
this->set_state_(improv::STATE_PROVISIONING);
|
||||||
ESP_LOGD(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
|
ESP_LOGD(TAG, "Received Improv Wi-Fi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
|
||||||
command.password.c_str());
|
command.password.c_str());
|
||||||
|
|
||||||
auto f = std::bind(&ESP32ImprovComponent::on_wifi_connect_timeout_, this);
|
auto f = std::bind(&ESP32ImprovComponent::on_wifi_connect_timeout_, this);
|
||||||
|
@ -313,7 +321,7 @@ void ESP32ImprovComponent::process_incoming_data_() {
|
||||||
this->incoming_data_.clear();
|
this->incoming_data_.clear();
|
||||||
}
|
}
|
||||||
} else if (this->incoming_data_.size() - 2 > length) {
|
} else if (this->incoming_data_.size() - 2 > length) {
|
||||||
ESP_LOGV(TAG, "Too much data came in, or malformed resetting buffer...");
|
ESP_LOGV(TAG, "Too much data received or data malformed; resetting buffer...");
|
||||||
this->incoming_data_.clear();
|
this->incoming_data_.clear();
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Waiting for split data packets...");
|
ESP_LOGV(TAG, "Waiting for split data packets...");
|
||||||
|
@ -327,7 +335,7 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() {
|
||||||
if (this->authorizer_ != nullptr)
|
if (this->authorizer_ != nullptr)
|
||||||
this->authorized_start_ = millis();
|
this->authorized_start_ = millis();
|
||||||
#endif
|
#endif
|
||||||
ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network");
|
ESP_LOGW(TAG, "Timed out while connecting to Wi-Fi network");
|
||||||
wifi::global_wifi_component->clear_sta();
|
wifi::global_wifi_component->clear_sta();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
#include "esphome/components/esp32_ble_server/ble_server.h"
|
#include "esphome/components/esp32_ble_server/ble_server.h"
|
||||||
#include "esphome/components/wifi/wifi_component.h"
|
#include "esphome/components/wifi/wifi_component.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -42,6 +46,11 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
|
||||||
void stop() override;
|
void stop() override;
|
||||||
bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
|
bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
void add_on_state_callback(std::function<void(improv::State, improv::Error)> &&callback) {
|
||||||
|
this->state_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }
|
void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }
|
||||||
#endif
|
#endif
|
||||||
|
@ -54,6 +63,9 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
|
||||||
void set_wifi_timeout(uint32_t wifi_timeout) { this->wifi_timeout_ = wifi_timeout; }
|
void set_wifi_timeout(uint32_t wifi_timeout) { this->wifi_timeout_ = wifi_timeout; }
|
||||||
uint32_t get_wifi_timeout() const { return this->wifi_timeout_; }
|
uint32_t get_wifi_timeout() const { return this->wifi_timeout_; }
|
||||||
|
|
||||||
|
improv::State get_improv_state() const { return this->state_; }
|
||||||
|
improv::Error get_improv_error_state() const { return this->error_state_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool should_start_{false};
|
bool should_start_{false};
|
||||||
bool setup_complete_{false};
|
bool setup_complete_{false};
|
||||||
|
@ -84,6 +96,9 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
|
||||||
|
|
||||||
improv::State state_{improv::STATE_STOPPED};
|
improv::State state_{improv::STATE_STOPPED};
|
||||||
improv::Error error_state_{improv::ERROR_NONE};
|
improv::Error error_state_{improv::ERROR_NONE};
|
||||||
|
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
|
CallbackManager<void(improv::State, improv::Error)> state_callback_{};
|
||||||
|
#endif
|
||||||
|
|
||||||
bool status_indicator_state_{false};
|
bool status_indicator_state_{false};
|
||||||
void set_status_indicator_state_(bool state);
|
void set_status_indicator_state_(bool state);
|
||||||
|
|
|
@ -59,6 +59,7 @@ ETHERNET_TYPES = {
|
||||||
"KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
|
"KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081,
|
||||||
"KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
|
"KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA,
|
||||||
"W5500": EthernetType.ETHERNET_TYPE_W5500,
|
"W5500": EthernetType.ETHERNET_TYPE_W5500,
|
||||||
|
"OPENETH": EthernetType.ETHERNET_TYPE_OPENETH,
|
||||||
}
|
}
|
||||||
|
|
||||||
SPI_ETHERNET_TYPES = ["W5500"]
|
SPI_ETHERNET_TYPES = ["W5500"]
|
||||||
|
@ -171,6 +172,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
"KSZ8081": RMII_SCHEMA,
|
"KSZ8081": RMII_SCHEMA,
|
||||||
"KSZ8081RNA": RMII_SCHEMA,
|
"KSZ8081RNA": RMII_SCHEMA,
|
||||||
"W5500": SPI_SCHEMA,
|
"W5500": SPI_SCHEMA,
|
||||||
|
"OPENETH": BASE_SCHEMA,
|
||||||
},
|
},
|
||||||
upper=True,
|
upper=True,
|
||||||
),
|
),
|
||||||
|
@ -240,6 +242,9 @@ async def to_code(config):
|
||||||
if CORE.using_esp_idf:
|
if CORE.using_esp_idf:
|
||||||
add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True)
|
add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True)
|
||||||
add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True)
|
add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True)
|
||||||
|
elif config[CONF_TYPE] == "OPENETH":
|
||||||
|
cg.add_define("USE_ETHERNET_OPENETH")
|
||||||
|
add_idf_sdkconfig_option("CONFIG_ETH_USE_OPENETH", True)
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
|
cg.add(var.set_phy_addr(config[CONF_PHY_ADDR]))
|
||||||
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
|
cg.add(var.set_mdc_pin(config[CONF_MDC_PIN]))
|
||||||
|
|
|
@ -120,6 +120,8 @@ void EthernetComponent::setup() {
|
||||||
phy_config.reset_gpio_num = this->reset_pin_;
|
phy_config.reset_gpio_num = this->reset_pin_;
|
||||||
|
|
||||||
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||||
|
#elif defined(USE_ETHERNET_OPENETH)
|
||||||
|
esp_eth_mac_t *mac = esp_eth_mac_new_openeth(&mac_config);
|
||||||
#else
|
#else
|
||||||
phy_config.phy_addr = this->phy_addr_;
|
phy_config.phy_addr = this->phy_addr_;
|
||||||
phy_config.reset_gpio_num = this->power_pin_;
|
phy_config.reset_gpio_num = this->power_pin_;
|
||||||
|
@ -143,6 +145,13 @@ void EthernetComponent::setup() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (this->type_) {
|
switch (this->type_) {
|
||||||
|
#ifdef USE_ETHERNET_OPENETH
|
||||||
|
case ETHERNET_TYPE_OPENETH: {
|
||||||
|
phy_config.autonego_timeout_ms = 1000;
|
||||||
|
this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#if CONFIG_ETH_USE_ESP32_EMAC
|
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||||
case ETHERNET_TYPE_LAN8720: {
|
case ETHERNET_TYPE_LAN8720: {
|
||||||
this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
|
this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
|
||||||
|
@ -302,6 +311,10 @@ void EthernetComponent::dump_config() {
|
||||||
eth_type = "W5500";
|
eth_type = "W5500";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ETHERNET_TYPE_OPENETH:
|
||||||
|
eth_type = "OPENETH";
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
eth_type = "Unknown";
|
eth_type = "Unknown";
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -25,6 +25,7 @@ enum EthernetType {
|
||||||
ETHERNET_TYPE_KSZ8081,
|
ETHERNET_TYPE_KSZ8081,
|
||||||
ETHERNET_TYPE_KSZ8081RNA,
|
ETHERNET_TYPE_KSZ8081RNA,
|
||||||
ETHERNET_TYPE_W5500,
|
ETHERNET_TYPE_W5500,
|
||||||
|
ETHERNET_TYPE_OPENETH,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ManualIP {
|
struct ManualIP {
|
||||||
|
|
0
esphome/components/gpio_expander/__init__.py
Normal file
0
esphome/components/gpio_expander/__init__.py
Normal file
38
esphome/components/gpio_expander/cached_gpio.h
Normal file
38
esphome/components/gpio_expander/cached_gpio.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace gpio_expander {
|
||||||
|
|
||||||
|
/// @brief A class to cache the read state of a GPIO expander.
|
||||||
|
template<typename T, T N> class CachedGpioExpander {
|
||||||
|
public:
|
||||||
|
bool digital_read(T pin) {
|
||||||
|
if (!this->read_cache_invalidated_[pin]) {
|
||||||
|
this->read_cache_invalidated_[pin] = true;
|
||||||
|
return this->digital_read_cache(pin);
|
||||||
|
}
|
||||||
|
return this->digital_read_hw(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void digital_write(T pin, bool value) { this->digital_write_hw(pin, value); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool digital_read_hw(T pin) = 0;
|
||||||
|
virtual bool digital_read_cache(T pin) = 0;
|
||||||
|
virtual void digital_write_hw(T pin, bool value) = 0;
|
||||||
|
|
||||||
|
void reset_pin_cache_() {
|
||||||
|
for (T i = 0; i < N; i++) {
|
||||||
|
this->read_cache_invalidated_[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<bool, N> read_cache_invalidated_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gpio_expander
|
||||||
|
} // namespace esphome
|
|
@ -114,7 +114,6 @@ SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
|
||||||
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
||||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||||
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
|
|
||||||
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +239,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
): cv.ensure_list(
|
): cv.ensure_list(
|
||||||
cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
|
cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_BEEPER, default=True): cv.boolean,
|
cv.Optional(CONF_BEEPER): cv.invalid(
|
||||||
|
f"The {CONF_BEEPER} option is deprecated, use beeper_on/beeper_off actions or beeper switch for a haier platform instead"
|
||||||
|
),
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
|
CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
|
||||||
): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
|
): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
|
||||||
|
@ -254,7 +255,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
|
): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_SUPPORTED_PRESETS,
|
CONF_SUPPORTED_PRESETS,
|
||||||
default=["BOOST", "ECO", "SLEEP"], # No AWAY by default
|
default=["BOOST", "SLEEP"], # No AWAY by default
|
||||||
): cv.ensure_list(
|
): cv.ensure_list(
|
||||||
cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
|
cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
|
||||||
),
|
),
|
||||||
|
|
|
@ -52,8 +52,6 @@ bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::stead
|
||||||
HaierClimateBase::HaierClimateBase()
|
HaierClimateBase::HaierClimateBase()
|
||||||
: haier_protocol_(*this),
|
: haier_protocol_(*this),
|
||||||
protocol_phase_(ProtocolPhases::SENDING_INIT_1),
|
protocol_phase_(ProtocolPhases::SENDING_INIT_1),
|
||||||
display_status_(true),
|
|
||||||
health_mode_(false),
|
|
||||||
force_send_control_(false),
|
force_send_control_(false),
|
||||||
forced_request_status_(false),
|
forced_request_status_(false),
|
||||||
reset_protocol_request_(false),
|
reset_protocol_request_(false),
|
||||||
|
@ -127,21 +125,34 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool HaierClimateBase::get_display_state() const { return this->display_status_; }
|
void HaierClimateBase::save_settings() {
|
||||||
|
HaierBaseSettings settings{this->get_health_mode(), this->get_display_state()};
|
||||||
|
if (!this->base_rtc_.save(&settings)) {
|
||||||
|
ESP_LOGW(TAG, "Failed to save settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HaierClimateBase::get_display_state() const {
|
||||||
|
return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
void HaierClimateBase::set_display_state(bool state) {
|
void HaierClimateBase::set_display_state(bool state) {
|
||||||
if (this->display_status_ != state) {
|
if (state != this->get_display_state()) {
|
||||||
this->display_status_ = state;
|
this->display_status_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
|
this->save_settings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HaierClimateBase::get_health_mode() const { return this->health_mode_; }
|
bool HaierClimateBase::get_health_mode() const {
|
||||||
|
return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
void HaierClimateBase::set_health_mode(bool state) {
|
void HaierClimateBase::set_health_mode(bool state) {
|
||||||
if (this->health_mode_ != state) {
|
if (state != this->get_health_mode()) {
|
||||||
this->health_mode_ = state;
|
this->health_mode_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
|
this->save_settings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +298,14 @@ void HaierClimateBase::loop() {
|
||||||
}
|
}
|
||||||
this->process_phase(now);
|
this->process_phase(now);
|
||||||
this->haier_protocol_.loop();
|
this->haier_protocol_.loop();
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
if ((this->display_switch_ != nullptr) && (this->display_switch_->state != this->get_display_state())) {
|
||||||
|
this->display_switch_->publish_state(this->get_display_state());
|
||||||
|
}
|
||||||
|
if ((this->health_mode_switch_ != nullptr) && (this->health_mode_switch_->state != this->get_health_mode())) {
|
||||||
|
this->health_mode_switch_->publish_state(this->get_health_mode());
|
||||||
|
}
|
||||||
|
#endif // USE_SWITCH
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaierClimateBase::process_protocol_reset() {
|
void HaierClimateBase::process_protocol_reset() {
|
||||||
|
@ -329,6 +348,26 @@ bool HaierClimateBase::prepare_pending_action() {
|
||||||
|
|
||||||
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||||
|
|
||||||
|
void HaierClimateBase::initialization() {
|
||||||
|
constexpr uint32_t restore_settings_version = 0xA77D21EF;
|
||||||
|
this->base_rtc_ =
|
||||||
|
global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||||
|
HaierBaseSettings recovered;
|
||||||
|
if (!this->base_rtc_.load(&recovered)) {
|
||||||
|
recovered = {false, true};
|
||||||
|
}
|
||||||
|
this->display_status_ = recovered.display_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
|
this->health_mode_ = recovered.health_mode ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
if (this->display_switch_ != nullptr) {
|
||||||
|
this->display_switch_->publish_state(this->get_display_state());
|
||||||
|
}
|
||||||
|
if (this->health_mode_switch_ != nullptr) {
|
||||||
|
this->health_mode_switch_->publish_state(this->get_health_mode());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void HaierClimateBase::control(const ClimateCall &call) {
|
void HaierClimateBase::control(const ClimateCall &call) {
|
||||||
ESP_LOGD("Control", "Control call");
|
ESP_LOGD("Control", "Control call");
|
||||||
if (!this->valid_connection()) {
|
if (!this->valid_connection()) {
|
||||||
|
@ -353,6 +392,22 @@ void HaierClimateBase::control(const ClimateCall &call) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
void HaierClimateBase::set_display_switch(switch_::Switch *sw) {
|
||||||
|
this->display_switch_ = sw;
|
||||||
|
if ((this->display_switch_ != nullptr) && (this->valid_connection())) {
|
||||||
|
this->display_switch_->publish_state(this->get_display_state());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HaierClimateBase::set_health_mode_switch(switch_::Switch *sw) {
|
||||||
|
this->health_mode_switch_ = sw;
|
||||||
|
if ((this->health_mode_switch_ != nullptr) && (this->valid_connection())) {
|
||||||
|
this->health_mode_switch_->publish_state(this->get_health_mode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void HaierClimateBase::HvacSettings::reset() {
|
void HaierClimateBase::HvacSettings::reset() {
|
||||||
this->valid = false;
|
this->valid = false;
|
||||||
this->mode.reset();
|
this->mode.reset();
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
// HaierProtocol
|
// HaierProtocol
|
||||||
#include <protocol/haier_protocol.h>
|
#include <protocol/haier_protocol.h>
|
||||||
|
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace haier {
|
namespace haier {
|
||||||
|
|
||||||
|
@ -20,10 +24,24 @@ enum class ActionRequest : uint8_t {
|
||||||
START_STERI_CLEAN = 5, // only hOn
|
START_STERI_CLEAN = 5, // only hOn
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HaierBaseSettings {
|
||||||
|
bool health_mode;
|
||||||
|
bool display_state;
|
||||||
|
};
|
||||||
|
|
||||||
class HaierClimateBase : public esphome::Component,
|
class HaierClimateBase : public esphome::Component,
|
||||||
public esphome::climate::Climate,
|
public esphome::climate::Climate,
|
||||||
public esphome::uart::UARTDevice,
|
public esphome::uart::UARTDevice,
|
||||||
public haier_protocol::ProtocolStream {
|
public haier_protocol::ProtocolStream {
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
public:
|
||||||
|
void set_display_switch(switch_::Switch *sw);
|
||||||
|
void set_health_mode_switch(switch_::Switch *sw);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
switch_::Switch *display_switch_{nullptr};
|
||||||
|
switch_::Switch *health_mode_switch_{nullptr};
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
HaierClimateBase();
|
HaierClimateBase();
|
||||||
HaierClimateBase(const HaierClimateBase &) = delete;
|
HaierClimateBase(const HaierClimateBase &) = delete;
|
||||||
|
@ -82,7 +100,8 @@ class HaierClimateBase : public esphome::Component,
|
||||||
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
||||||
virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
|
virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
|
||||||
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming)
|
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming)
|
||||||
virtual void initialization(){};
|
virtual void save_settings();
|
||||||
|
virtual void initialization();
|
||||||
virtual bool prepare_pending_action();
|
virtual bool prepare_pending_action();
|
||||||
virtual void process_protocol_reset();
|
virtual void process_protocol_reset();
|
||||||
esphome::climate::ClimateTraits traits() override;
|
esphome::climate::ClimateTraits traits() override;
|
||||||
|
@ -127,13 +146,19 @@ class HaierClimateBase : public esphome::Component,
|
||||||
ActionRequest action;
|
ActionRequest action;
|
||||||
esphome::optional<haier_protocol::HaierMessage> message;
|
esphome::optional<haier_protocol::HaierMessage> message;
|
||||||
};
|
};
|
||||||
|
enum class SwitchState {
|
||||||
|
OFF = 0b00,
|
||||||
|
ON = 0b01,
|
||||||
|
PENDING_OFF = 0b10,
|
||||||
|
PENDING_ON = 0b11,
|
||||||
|
};
|
||||||
haier_protocol::ProtocolHandler haier_protocol_;
|
haier_protocol::ProtocolHandler haier_protocol_;
|
||||||
ProtocolPhases protocol_phase_;
|
ProtocolPhases protocol_phase_;
|
||||||
esphome::optional<PendingAction> action_request_;
|
esphome::optional<PendingAction> action_request_;
|
||||||
uint8_t fan_mode_speed_;
|
uint8_t fan_mode_speed_;
|
||||||
uint8_t other_modes_fan_speed_;
|
uint8_t other_modes_fan_speed_;
|
||||||
bool display_status_;
|
SwitchState display_status_{SwitchState::ON};
|
||||||
bool health_mode_;
|
SwitchState health_mode_{SwitchState::OFF};
|
||||||
bool force_send_control_;
|
bool force_send_control_;
|
||||||
bool forced_request_status_;
|
bool forced_request_status_;
|
||||||
bool reset_protocol_request_;
|
bool reset_protocol_request_;
|
||||||
|
@ -148,6 +173,7 @@ class HaierClimateBase : public esphome::Component,
|
||||||
std::chrono::steady_clock::time_point last_status_request_; // To request AC status
|
std::chrono::steady_clock::time_point last_status_request_; // To request AC status
|
||||||
std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level
|
std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level
|
||||||
CallbackManager<void(const char *, size_t)> status_message_callback_{};
|
CallbackManager<void(const char *, size_t)> status_message_callback_{};
|
||||||
|
ESPPreferenceObject base_rtc_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatusMessageTrigger : public Trigger<const char *, size_t> {
|
class StatusMessageTrigger : public Trigger<const char *, size_t> {
|
||||||
|
|
|
@ -31,9 +31,32 @@ HonClimate::HonClimate()
|
||||||
|
|
||||||
HonClimate::~HonClimate() {}
|
HonClimate::~HonClimate() {}
|
||||||
|
|
||||||
void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; }
|
void HonClimate::set_beeper_state(bool state) {
|
||||||
|
if (state != this->settings_.beeper_state) {
|
||||||
|
this->settings_.beeper_state = state;
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
this->beeper_switch_->publish_state(state);
|
||||||
|
#endif
|
||||||
|
this->hon_rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
|
bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; }
|
||||||
|
|
||||||
|
void HonClimate::set_quiet_mode_state(bool state) {
|
||||||
|
if (state != this->get_quiet_mode_state()) {
|
||||||
|
this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
|
this->settings_.quiet_mode_state = state;
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
this->quiet_mode_switch_->publish_state(state);
|
||||||
|
#endif
|
||||||
|
this->hon_rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HonClimate::get_quiet_mode_state() const {
|
||||||
|
return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
|
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
|
||||||
return this->current_vertical_swing_;
|
return this->current_vertical_swing_;
|
||||||
|
@ -474,16 +497,19 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HonClimate::initialization() {
|
void HonClimate::initialization() {
|
||||||
constexpr uint32_t restore_settings_version = 0xE834D8DCUL;
|
HaierClimateBase::initialization();
|
||||||
this->rtc_ = global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
|
||||||
|
this->hon_rtc_ =
|
||||||
|
global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||||
HonSettings recovered;
|
HonSettings recovered;
|
||||||
if (this->rtc_.load(&recovered)) {
|
if (this->hon_rtc_.load(&recovered)) {
|
||||||
this->settings_ = recovered;
|
this->settings_ = recovered;
|
||||||
} else {
|
} else {
|
||||||
this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER};
|
this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER, true, false};
|
||||||
}
|
}
|
||||||
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
|
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
|
||||||
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
|
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
|
||||||
|
this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
|
@ -519,8 +545,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
out_data->ac_power = 1;
|
out_data->ac_power = 1;
|
||||||
out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
|
out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
|
||||||
out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode
|
out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode
|
||||||
// Disabling boost and eco mode for Fan only
|
// Disabling boost for Fan only
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_MODE_COOL:
|
case CLIMATE_MODE_COOL:
|
||||||
|
@ -582,47 +607,34 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
}
|
}
|
||||||
if (out_data->ac_power == 0) {
|
if (out_data->ac_power == 0) {
|
||||||
// If AC is off - no presets allowed
|
// If AC is off - no presets allowed
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
} else if (climate_control.preset.has_value()) {
|
} else if (climate_control.preset.has_value()) {
|
||||||
switch (climate_control.preset.value()) {
|
switch (climate_control.preset.value()) {
|
||||||
case CLIMATE_PRESET_NONE:
|
case CLIMATE_PRESET_NONE:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
|
||||||
out_data->sleep_mode = 0;
|
|
||||||
out_data->ten_degree = 0;
|
|
||||||
break;
|
|
||||||
case CLIMATE_PRESET_ECO:
|
|
||||||
// Eco is not supported in Fan only mode
|
|
||||||
out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_BOOST:
|
case CLIMATE_PRESET_BOOST:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
// Boost is not supported in Fan only mode
|
// Boost is not supported in Fan only mode
|
||||||
out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_AWAY:
|
case CLIMATE_PRESET_AWAY:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
// 10 degrees allowed only in heat mode
|
// 10 degrees allowed only in heat mode
|
||||||
out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
|
out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_SLEEP:
|
case CLIMATE_PRESET_SLEEP:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 1;
|
out_data->sleep_mode = 1;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGE("Control", "Unsupported preset");
|
ESP_LOGE("Control", "Unsupported preset");
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
|
@ -638,10 +650,23 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
|
out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
|
||||||
this->pending_horizontal_direction_.reset();
|
this->pending_horizontal_direction_.reset();
|
||||||
}
|
}
|
||||||
out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
|
{
|
||||||
|
// Quiet mode
|
||||||
|
if ((out_data->ac_power == 0) || (out_data->ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN)) {
|
||||||
|
// If AC is off or in fan only mode - no quiet mode allowed
|
||||||
|
out_data->quiet_mode = 0;
|
||||||
|
} else {
|
||||||
|
out_data->quiet_mode = this->get_quiet_mode_state() ? 1 : 0;
|
||||||
|
}
|
||||||
|
// Clean quiet mode state pending flag
|
||||||
|
this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
|
||||||
|
}
|
||||||
|
out_data->beeper_status = ((!this->get_beeper_state()) || (!has_hvac_settings)) ? 1 : 0;
|
||||||
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
||||||
out_data->display_status = this->display_status_ ? 1 : 0;
|
out_data->display_status = this->get_display_state() ? 1 : 0;
|
||||||
out_data->health_mode = this->health_mode_ ? 1 : 0;
|
this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
|
||||||
|
out_data->health_mode = this->get_health_mode() ? 1 : 0;
|
||||||
|
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
|
||||||
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
|
(uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
|
||||||
control_out_buffer, this->real_control_packet_size_);
|
control_out_buffer, this->real_control_packet_size_);
|
||||||
|
@ -765,6 +790,22 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri
|
||||||
}
|
}
|
||||||
#endif // USE_TEXT_SENSOR
|
#endif // USE_TEXT_SENSOR
|
||||||
|
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
void HonClimate::set_beeper_switch(switch_::Switch *sw) {
|
||||||
|
this->beeper_switch_ = sw;
|
||||||
|
if (this->beeper_switch_ != nullptr) {
|
||||||
|
this->beeper_switch_->publish_state(this->get_beeper_state());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HonClimate::set_quiet_mode_switch(switch_::Switch *sw) {
|
||||||
|
this->quiet_mode_switch_ = sw;
|
||||||
|
if (this->quiet_mode_switch_ != nullptr) {
|
||||||
|
this->quiet_mode_switch_->publish_state(this->settings_.quiet_mode_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_SWITCH
|
||||||
|
|
||||||
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
|
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
|
||||||
size_t expected_size =
|
size_t expected_size =
|
||||||
2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
|
2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
|
||||||
|
@ -827,9 +868,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
{
|
{
|
||||||
// Extra modes/presets
|
// Extra modes/presets
|
||||||
optional<ClimatePreset> old_preset = this->preset;
|
optional<ClimatePreset> old_preset = this->preset;
|
||||||
if (packet.control.quiet_mode != 0) {
|
if (packet.control.fast_mode != 0) {
|
||||||
this->preset = CLIMATE_PRESET_ECO;
|
|
||||||
} else if (packet.control.fast_mode != 0) {
|
|
||||||
this->preset = CLIMATE_PRESET_BOOST;
|
this->preset = CLIMATE_PRESET_BOOST;
|
||||||
} else if (packet.control.sleep_mode != 0) {
|
} else if (packet.control.sleep_mode != 0) {
|
||||||
this->preset = CLIMATE_PRESET_SLEEP;
|
this->preset = CLIMATE_PRESET_SLEEP;
|
||||||
|
@ -883,28 +922,26 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// Display status
|
// Display status
|
||||||
// should be before "Climate mode" because it is changing this->mode
|
// should be before "Climate mode" because it is changing this->mode
|
||||||
if (packet.control.ac_power != 0) {
|
if (packet.control.ac_power != 0) {
|
||||||
// if AC is off display status always ON so process it only when AC is on
|
// if AC is off display status always ON so process it only when AC is on
|
||||||
bool disp_status = packet.control.display_status != 0;
|
bool disp_status = packet.control.display_status != 0;
|
||||||
if (disp_status != this->display_status_) {
|
if (disp_status != this->get_display_state()) {
|
||||||
// Do something only if display status changed
|
// Do something only if display status changed
|
||||||
if (this->mode == CLIMATE_MODE_OFF) {
|
if (this->mode == CLIMATE_MODE_OFF) {
|
||||||
// AC just turned on from remote need to turn off display
|
// AC just turned on from remote need to turn off display
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
} else {
|
} else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->display_status_ = disp_status;
|
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
{
|
|
||||||
// Health mode
|
// Health mode
|
||||||
bool old_health_mode = this->health_mode_;
|
if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->health_mode_ = packet.control.health_mode == 1;
|
bool old_health_mode = this->get_health_mode();
|
||||||
should_publish = should_publish || (old_health_mode != this->health_mode_);
|
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
|
||||||
|
should_publish = should_publish || (old_health_mode != this->get_health_mode());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
CleaningState new_cleaning;
|
CleaningState new_cleaning;
|
||||||
|
@ -958,17 +995,36 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (old_mode != this->mode);
|
should_publish = should_publish || (old_mode != this->mode);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Quiet mode, should be after climate mode
|
||||||
|
if ((this->mode != CLIMATE_MODE_FAN_ONLY) && (this->mode != CLIMATE_MODE_OFF) &&
|
||||||
|
((((uint8_t) this->quiet_mode_state_) & 0b10) == 0)) {
|
||||||
|
// In proper mode and not in pending state
|
||||||
|
bool new_quiet_mode = packet.control.quiet_mode != 0;
|
||||||
|
if (new_quiet_mode != this->get_quiet_mode_state()) {
|
||||||
|
this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF;
|
||||||
|
this->settings_.quiet_mode_state = new_quiet_mode;
|
||||||
|
this->hon_rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// Swing mode
|
// Swing mode
|
||||||
ClimateSwingMode old_swing_mode = this->swing_mode;
|
ClimateSwingMode old_swing_mode = this->swing_mode;
|
||||||
if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) {
|
const std::set<ClimateSwingMode> &swing_modes = traits_.get_supported_swing_modes();
|
||||||
if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
|
bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end();
|
||||||
|
bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end();
|
||||||
|
if (horizontal_swing_supported &&
|
||||||
|
(packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) {
|
||||||
|
if (vertical_swing_supported &&
|
||||||
|
(packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
|
||||||
this->swing_mode = CLIMATE_SWING_BOTH;
|
this->swing_mode = CLIMATE_SWING_BOTH;
|
||||||
} else {
|
} else {
|
||||||
this->swing_mode = CLIMATE_SWING_HORIZONTAL;
|
this->swing_mode = CLIMATE_SWING_HORIZONTAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
|
if (vertical_swing_supported &&
|
||||||
|
(packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
|
||||||
this->swing_mode = CLIMATE_SWING_VERTICAL;
|
this->swing_mode = CLIMATE_SWING_VERTICAL;
|
||||||
} else {
|
} else {
|
||||||
this->swing_mode = CLIMATE_SWING_OFF;
|
this->swing_mode = CLIMATE_SWING_OFF;
|
||||||
|
@ -985,7 +1041,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
if (save_settings) {
|
if (save_settings) {
|
||||||
this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
|
this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
|
||||||
this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
|
this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
|
||||||
this->rtc_.save(&this->settings_);
|
this->hon_rtc_.save(&this->settings_);
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (old_swing_mode != this->swing_mode);
|
should_publish = should_publish || (old_swing_mode != this->swing_mode);
|
||||||
}
|
}
|
||||||
|
@ -1017,7 +1073,7 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
||||||
(uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
|
(uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
|
||||||
this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2));
|
this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2));
|
||||||
}
|
}
|
||||||
// Health mode
|
// Health mode
|
||||||
{
|
{
|
||||||
|
@ -1025,13 +1081,16 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
||||||
(uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
|
(uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
|
||||||
this->health_mode_ ? ONE_BUF : ZERO_BUF, 2));
|
this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2));
|
||||||
|
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
|
||||||
}
|
}
|
||||||
// Climate mode
|
// Climate mode
|
||||||
|
ClimateMode climate_mode = this->mode;
|
||||||
bool new_power = this->mode != CLIMATE_MODE_OFF;
|
bool new_power = this->mode != CLIMATE_MODE_OFF;
|
||||||
uint8_t fan_mode_buf[] = {0x00, 0xFF};
|
uint8_t fan_mode_buf[] = {0x00, 0xFF};
|
||||||
uint8_t quiet_mode_buf[] = {0x00, 0xFF};
|
uint8_t quiet_mode_buf[] = {0x00, 0xFF};
|
||||||
if (climate_control.mode.has_value()) {
|
if (climate_control.mode.has_value()) {
|
||||||
|
climate_mode = climate_control.mode.value();
|
||||||
uint8_t buffer[2] = {0x00, 0x00};
|
uint8_t buffer[2] = {0x00, 0x00};
|
||||||
switch (climate_control.mode.value()) {
|
switch (climate_control.mode.value()) {
|
||||||
case CLIMATE_MODE_OFF:
|
case CLIMATE_MODE_OFF:
|
||||||
|
@ -1076,8 +1135,6 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
(uint8_t) hon_protocol::DataParameters::AC_MODE,
|
(uint8_t) hon_protocol::DataParameters::AC_MODE,
|
||||||
buffer, 2));
|
buffer, 2));
|
||||||
fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
|
fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
|
||||||
// Disabling eco mode for Fan only
|
|
||||||
quiet_mode_buf[1] = 0;
|
|
||||||
break;
|
break;
|
||||||
case CLIMATE_MODE_COOL:
|
case CLIMATE_MODE_COOL:
|
||||||
new_power = true;
|
new_power = true;
|
||||||
|
@ -1108,30 +1165,20 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
uint8_t away_mode_buf[] = {0x00, 0xFF};
|
uint8_t away_mode_buf[] = {0x00, 0xFF};
|
||||||
if (!new_power) {
|
if (!new_power) {
|
||||||
// If AC is off - no presets allowed
|
// If AC is off - no presets allowed
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
fast_mode_buf[1] = 0x00;
|
fast_mode_buf[1] = 0x00;
|
||||||
away_mode_buf[1] = 0x00;
|
away_mode_buf[1] = 0x00;
|
||||||
} else if (climate_control.preset.has_value()) {
|
} else if (climate_control.preset.has_value()) {
|
||||||
switch (climate_control.preset.value()) {
|
switch (climate_control.preset.value()) {
|
||||||
case CLIMATE_PRESET_NONE:
|
case CLIMATE_PRESET_NONE:
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
fast_mode_buf[1] = 0x00;
|
|
||||||
away_mode_buf[1] = 0x00;
|
|
||||||
break;
|
|
||||||
case CLIMATE_PRESET_ECO:
|
|
||||||
// Eco is not supported in Fan only mode
|
|
||||||
quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
|
||||||
fast_mode_buf[1] = 0x00;
|
fast_mode_buf[1] = 0x00;
|
||||||
away_mode_buf[1] = 0x00;
|
away_mode_buf[1] = 0x00;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_BOOST:
|
case CLIMATE_PRESET_BOOST:
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
// Boost is not supported in Fan only mode
|
// Boost is not supported in Fan only mode
|
||||||
fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
||||||
away_mode_buf[1] = 0x00;
|
away_mode_buf[1] = 0x00;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_AWAY:
|
case CLIMATE_PRESET_AWAY:
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
fast_mode_buf[1] = 0x00;
|
fast_mode_buf[1] = 0x00;
|
||||||
away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
|
away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
|
||||||
break;
|
break;
|
||||||
|
@ -1140,8 +1187,18 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Quiet mode
|
||||||
|
if (new_power && (climate_mode != CLIMATE_MODE_FAN_ONLY) && this->get_quiet_mode_state()) {
|
||||||
|
quiet_mode_buf[1] = 0x01;
|
||||||
|
} else {
|
||||||
|
quiet_mode_buf[1] = 0x00;
|
||||||
|
}
|
||||||
|
// Clean quiet mode state pending flag
|
||||||
|
this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
|
||||||
|
}
|
||||||
auto presets = this->traits_.get_supported_presets();
|
auto presets = this->traits_.get_supported_presets();
|
||||||
if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) {
|
if (quiet_mode_buf[1] != 0xFF) {
|
||||||
this->control_messages_queue_.push(
|
this->control_messages_queue_.push(
|
||||||
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
#include "esphome/components/text_sensor/text_sensor.h"
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#endif
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "haier_base.h"
|
#include "haier_base.h"
|
||||||
#include "hon_packet.h"
|
#include "hon_packet.h"
|
||||||
|
@ -28,6 +31,8 @@ enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE
|
||||||
struct HonSettings {
|
struct HonSettings {
|
||||||
hon_protocol::VerticalSwingMode last_vertiacal_swing;
|
hon_protocol::VerticalSwingMode last_vertiacal_swing;
|
||||||
hon_protocol::HorizontalSwingMode last_horizontal_swing;
|
hon_protocol::HorizontalSwingMode last_horizontal_swing;
|
||||||
|
bool beeper_state;
|
||||||
|
bool quiet_mode_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HonClimate : public HaierClimateBase {
|
class HonClimate : public HaierClimateBase {
|
||||||
|
@ -86,6 +91,15 @@ class HonClimate : public HaierClimateBase {
|
||||||
protected:
|
protected:
|
||||||
void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
|
void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
|
||||||
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
public:
|
||||||
|
void set_beeper_switch(switch_::Switch *sw);
|
||||||
|
void set_quiet_mode_switch(switch_::Switch *sw);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
switch_::Switch *beeper_switch_{nullptr};
|
||||||
|
switch_::Switch *quiet_mode_switch_{nullptr};
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
HonClimate();
|
HonClimate();
|
||||||
|
@ -95,6 +109,8 @@ class HonClimate : public HaierClimateBase {
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_beeper_state(bool state);
|
void set_beeper_state(bool state);
|
||||||
bool get_beeper_state() const;
|
bool get_beeper_state() const;
|
||||||
|
void set_quiet_mode_state(bool state);
|
||||||
|
bool get_quiet_mode_state() const;
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
|
esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
|
||||||
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
||||||
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
||||||
|
@ -153,7 +169,6 @@ class HonClimate : public HaierClimateBase {
|
||||||
bool functions_[5];
|
bool functions_[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
bool beeper_status_;
|
|
||||||
CleaningState cleaning_status_;
|
CleaningState cleaning_status_;
|
||||||
bool got_valid_outdoor_temp_;
|
bool got_valid_outdoor_temp_;
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
|
esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
|
||||||
|
@ -175,7 +190,8 @@ class HonClimate : public HaierClimateBase {
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
|
esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
|
||||||
esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
|
esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
|
||||||
HonSettings settings_;
|
HonSettings settings_;
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject hon_rtc_;
|
||||||
|
SwitchState quiet_mode_state_{SwitchState::OFF};
|
||||||
};
|
};
|
||||||
|
|
||||||
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
||||||
|
|
|
@ -376,8 +376,10 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_data->display_status = this->display_status_ ? 0 : 1;
|
out_data->display_status = this->get_display_state() ? 0 : 1;
|
||||||
out_data->health_mode = this->health_mode_ ? 1 : 0;
|
this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
|
||||||
|
out_data->health_mode = this->get_health_mode() ? 1 : 0;
|
||||||
|
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
|
||||||
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer,
|
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer,
|
||||||
sizeof(smartair2_protocol::HaierPacketControl));
|
sizeof(smartair2_protocol::HaierPacketControl));
|
||||||
}
|
}
|
||||||
|
@ -446,28 +448,26 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// Display status
|
// Display status
|
||||||
// should be before "Climate mode" because it is changing this->mode
|
// should be before "Climate mode" because it is changing this->mode
|
||||||
if (packet.control.ac_power != 0) {
|
if (packet.control.ac_power != 0) {
|
||||||
// if AC is off display status always ON so process it only when AC is on
|
// if AC is off display status always ON so process it only when AC is on
|
||||||
bool disp_status = packet.control.display_status == 0;
|
bool disp_status = packet.control.display_status == 0;
|
||||||
if (disp_status != this->display_status_) {
|
if (disp_status != this->get_display_state()) {
|
||||||
// Do something only if display status changed
|
// Do something only if display status changed
|
||||||
if (this->mode == CLIMATE_MODE_OFF) {
|
if (this->mode == CLIMATE_MODE_OFF) {
|
||||||
// AC just turned on from remote need to turn off display
|
// AC just turned on from remote need to turn off display
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
} else {
|
} else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->display_status_ = disp_status;
|
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
{
|
|
||||||
// Health mode
|
// Health mode
|
||||||
bool old_health_mode = this->health_mode_;
|
if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->health_mode_ = packet.control.health_mode == 1;
|
bool old_health_mode = this->get_health_mode();
|
||||||
should_publish = should_publish || (old_health_mode != this->health_mode_);
|
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
|
||||||
|
should_publish = should_publish || (old_health_mode != this->get_health_mode());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Climate mode
|
// Climate mode
|
||||||
|
|
91
esphome/components/haier/switch/__init__.py
Normal file
91
esphome/components/haier/switch/__init__.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
from esphome.components import switch
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_BEEPER,
|
||||||
|
CONF_DISPLAY,
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
)
|
||||||
|
from ..climate import (
|
||||||
|
CONF_HAIER_ID,
|
||||||
|
CONF_PROTOCOL,
|
||||||
|
HaierClimateBase,
|
||||||
|
haier_ns,
|
||||||
|
PROTOCOL_HON,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@paveldn"]
|
||||||
|
BeeperSwitch = haier_ns.class_("BeeperSwitch", switch.Switch)
|
||||||
|
HealthModeSwitch = haier_ns.class_("HealthModeSwitch", switch.Switch)
|
||||||
|
DisplaySwitch = haier_ns.class_("DisplaySwitch", switch.Switch)
|
||||||
|
QuietModeSwitch = haier_ns.class_("QuietModeSwitch", switch.Switch)
|
||||||
|
|
||||||
|
# Haier switches
|
||||||
|
CONF_HEALTH_MODE = "health_mode"
|
||||||
|
CONF_QUIET_MODE = "quiet_mode"
|
||||||
|
|
||||||
|
# Additional icons
|
||||||
|
ICON_LEAF = "mdi:leaf"
|
||||||
|
ICON_LED_ON = "mdi:led-on"
|
||||||
|
ICON_VOLUME_HIGH = "mdi:volume-high"
|
||||||
|
ICON_VOLUME_OFF = "mdi:volume-off"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_HAIER_ID): cv.use_id(HaierClimateBase),
|
||||||
|
cv.Optional(CONF_DISPLAY): switch.switch_schema(
|
||||||
|
DisplaySwitch,
|
||||||
|
icon=ICON_LED_ON,
|
||||||
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEALTH_MODE): switch.switch_schema(
|
||||||
|
HealthModeSwitch,
|
||||||
|
icon=ICON_LEAF,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
# Beeper switch is only supported for HonClimate
|
||||||
|
cv.Optional(CONF_BEEPER): switch.switch_schema(
|
||||||
|
BeeperSwitch,
|
||||||
|
icon=ICON_VOLUME_HIGH,
|
||||||
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
# Quiet mode is only supported for HonClimate
|
||||||
|
cv.Optional(CONF_QUIET_MODE): switch.switch_schema(
|
||||||
|
QuietModeSwitch,
|
||||||
|
icon=ICON_VOLUME_OFF,
|
||||||
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _final_validate(config):
|
||||||
|
full_config = fv.full_config.get()
|
||||||
|
for switch_type in [CONF_BEEPER, CONF_QUIET_MODE]:
|
||||||
|
# Check switches that are only supported for HonClimate
|
||||||
|
if config.get(switch_type):
|
||||||
|
climate_path = full_config.get_path_for_id(config[CONF_HAIER_ID])[:-1]
|
||||||
|
climate_conf = full_config.get_config_for_path(climate_path)
|
||||||
|
protocol_type = climate_conf.get(CONF_PROTOCOL)
|
||||||
|
if protocol_type.casefold() != PROTOCOL_HON.casefold():
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{switch_type} switch is only supported for hon climate"
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
parent = await cg.get_variable(config[CONF_HAIER_ID])
|
||||||
|
|
||||||
|
for switch_type in [CONF_DISPLAY, CONF_HEALTH_MODE, CONF_BEEPER, CONF_QUIET_MODE]:
|
||||||
|
if conf := config.get(switch_type):
|
||||||
|
sw_var = await switch.new_switch(conf)
|
||||||
|
await cg.register_parented(sw_var, parent)
|
||||||
|
cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var))
|
14
esphome/components/haier/switch/beeper.cpp
Normal file
14
esphome/components/haier/switch/beeper.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "beeper.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void BeeperSwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_beeper_state() != state) {
|
||||||
|
this->parent_->set_beeper_state(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/beeper.h
Normal file
18
esphome/components/haier/switch/beeper.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../hon_climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class BeeperSwitch : public switch_::Switch, public Parented<HonClimate> {
|
||||||
|
public:
|
||||||
|
BeeperSwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
14
esphome/components/haier/switch/display.cpp
Normal file
14
esphome/components/haier/switch/display.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void DisplaySwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_display_state() != state) {
|
||||||
|
this->parent_->set_display_state(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/display.h
Normal file
18
esphome/components/haier/switch/display.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../haier_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class DisplaySwitch : public switch_::Switch, public Parented<HaierClimateBase> {
|
||||||
|
public:
|
||||||
|
DisplaySwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
14
esphome/components/haier/switch/health_mode.cpp
Normal file
14
esphome/components/haier/switch/health_mode.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "health_mode.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void HealthModeSwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_health_mode() != state) {
|
||||||
|
this->parent_->set_health_mode(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/health_mode.h
Normal file
18
esphome/components/haier/switch/health_mode.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../haier_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class HealthModeSwitch : public switch_::Switch, public Parented<HaierClimateBase> {
|
||||||
|
public:
|
||||||
|
HealthModeSwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
14
esphome/components/haier/switch/quiet_mode.cpp
Normal file
14
esphome/components/haier/switch/quiet_mode.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "quiet_mode.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void QuietModeSwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_quiet_mode_state() != state) {
|
||||||
|
this->parent_->set_quiet_mode_state(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/quiet_mode.h
Normal file
18
esphome/components/haier/switch/quiet_mode.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../hon_climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class QuietModeSwitch : public switch_::Switch, public Parented<HonClimate> {
|
||||||
|
public:
|
||||||
|
QuietModeSwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
|
@ -25,6 +25,7 @@ I2SAudioSpeaker = i2s_audio_ns.class_(
|
||||||
|
|
||||||
|
|
||||||
CONF_DAC_TYPE = "dac_type"
|
CONF_DAC_TYPE = "dac_type"
|
||||||
|
CONF_I2S_COMM_FMT = "i2s_comm_fmt"
|
||||||
|
|
||||||
i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
|
i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
|
||||||
INTERNAL_DAC_OPTIONS = {
|
INTERNAL_DAC_OPTIONS = {
|
||||||
|
@ -33,6 +34,20 @@ INTERNAL_DAC_OPTIONS = {
|
||||||
CONF_STEREO: i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN,
|
CONF_STEREO: i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i2s_comm_format_t = cg.global_ns.enum("i2s_comm_format_t")
|
||||||
|
I2C_COMM_FMT_OPTIONS = {
|
||||||
|
"stand_i2s": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_I2S,
|
||||||
|
"stand_msb": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_MSB,
|
||||||
|
"stand_pcm_short": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_PCM_SHORT,
|
||||||
|
"stand_pcm_long": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_PCM_LONG,
|
||||||
|
"stand_max": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_MAX,
|
||||||
|
"i2s_msb": i2s_comm_format_t.I2S_COMM_FORMAT_I2S_MSB,
|
||||||
|
"i2s_lsb": i2s_comm_format_t.I2S_COMM_FORMAT_I2S_LSB,
|
||||||
|
"pcm": i2s_comm_format_t.I2S_COMM_FORMAT_PCM,
|
||||||
|
"pcm_short": i2s_comm_format_t.I2S_COMM_FORMAT_PCM_SHORT,
|
||||||
|
"pcm_long": i2s_comm_format_t.I2S_COMM_FORMAT_PCM_LONG,
|
||||||
|
}
|
||||||
|
|
||||||
NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2]
|
NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2]
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,6 +92,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Required(
|
cv.Required(
|
||||||
CONF_I2S_DOUT_PIN
|
CONF_I2S_DOUT_PIN
|
||||||
): pins.internal_gpio_output_pin_number,
|
): pins.internal_gpio_output_pin_number,
|
||||||
|
cv.Optional(CONF_I2S_COMM_FMT, default="stand_i2s"): cv.enum(
|
||||||
|
I2C_COMM_FMT_OPTIONS, lower=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -96,4 +114,5 @@ async def to_code(config):
|
||||||
cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL]))
|
cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL]))
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
|
cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
|
||||||
|
cg.add(var.set_i2s_comm_fmt(config[CONF_I2S_COMM_FMT]))
|
||||||
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
|
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
|
||||||
|
|
|
@ -83,7 +83,7 @@ void I2SAudioSpeaker::player_task(void *params) {
|
||||||
.sample_rate = this_speaker->sample_rate_,
|
.sample_rate = this_speaker->sample_rate_,
|
||||||
.bits_per_sample = this_speaker->bits_per_sample_,
|
.bits_per_sample = this_speaker->bits_per_sample_,
|
||||||
.channel_format = this_speaker->channel_,
|
.channel_format = this_speaker->channel_,
|
||||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
.communication_format = this_speaker->i2s_comm_fmt_,
|
||||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||||
.dma_buf_count = 8,
|
.dma_buf_count = 8,
|
||||||
.dma_buf_len = 256,
|
.dma_buf_len = 256,
|
||||||
|
|
|
@ -49,6 +49,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
|
||||||
#if SOC_I2S_SUPPORTS_DAC
|
#if SOC_I2S_SUPPORTS_DAC
|
||||||
void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; }
|
void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; }
|
||||||
#endif
|
#endif
|
||||||
|
void set_i2s_comm_fmt(i2s_comm_format_t mode) { this->i2s_comm_fmt_ = mode; }
|
||||||
|
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
@ -76,6 +77,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
|
||||||
#if SOC_I2S_SUPPORTS_DAC
|
#if SOC_I2S_SUPPORTS_DAC
|
||||||
i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE};
|
i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE};
|
||||||
#endif
|
#endif
|
||||||
|
i2s_comm_format_t i2s_comm_fmt_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace i2s_audio
|
} // namespace i2s_audio
|
||||||
|
|
|
@ -53,6 +53,8 @@ MODELS = {
|
||||||
"inkplate_10": InkplateModel.INKPLATE_10,
|
"inkplate_10": InkplateModel.INKPLATE_10,
|
||||||
"inkplate_6_plus": InkplateModel.INKPLATE_6_PLUS,
|
"inkplate_6_plus": InkplateModel.INKPLATE_6_PLUS,
|
||||||
"inkplate_6_v2": InkplateModel.INKPLATE_6_V2,
|
"inkplate_6_v2": InkplateModel.INKPLATE_6_V2,
|
||||||
|
"inkplate_5": InkplateModel.INKPLATE_5,
|
||||||
|
"inkplate_5_v2": InkplateModel.INKPLATE_5_V2,
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
|
|
@ -15,6 +15,8 @@ enum InkplateModel : uint8_t {
|
||||||
INKPLATE_10 = 1,
|
INKPLATE_10 = 1,
|
||||||
INKPLATE_6_PLUS = 2,
|
INKPLATE_6_PLUS = 2,
|
||||||
INKPLATE_6_V2 = 3,
|
INKPLATE_6_V2 = 3,
|
||||||
|
INKPLATE_5 = 4,
|
||||||
|
INKPLATE_5_V2 = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||||
|
@ -29,7 +31,7 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||||
const uint8_t pixelMaskLUT[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
const uint8_t pixelMaskLUT[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
||||||
const uint8_t pixelMaskGLUT[2] = {0x0F, 0xF0};
|
const uint8_t pixelMaskGLUT[2] = {0x0F, 0xF0};
|
||||||
|
|
||||||
const uint8_t waveform3BitAll[4][8][9] = {// INKPLATE_6
|
const uint8_t waveform3BitAll[6][8][9] = {// INKPLATE_6
|
||||||
{{0, 1, 1, 0, 0, 1, 1, 0, 0},
|
{{0, 1, 1, 0, 0, 1, 1, 0, 0},
|
||||||
{0, 1, 2, 1, 1, 2, 1, 0, 0},
|
{0, 1, 2, 1, 1, 2, 1, 0, 0},
|
||||||
{1, 1, 1, 2, 2, 1, 0, 0, 0},
|
{1, 1, 1, 2, 2, 1, 0, 0, 0},
|
||||||
|
@ -64,7 +66,25 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||||
{1, 1, 1, 1, 2, 2, 1, 0, 0},
|
{1, 1, 1, 1, 2, 2, 1, 0, 0},
|
||||||
{0, 1, 1, 1, 2, 2, 1, 0, 0},
|
{0, 1, 1, 1, 2, 2, 1, 0, 0},
|
||||||
{0, 0, 0, 0, 1, 1, 2, 0, 0},
|
{0, 0, 0, 0, 1, 1, 2, 0, 0},
|
||||||
{0, 0, 0, 0, 0, 1, 2, 0, 0}}};
|
{0, 0, 0, 0, 0, 1, 2, 0, 0}},
|
||||||
|
// INKPLATE_5
|
||||||
|
{{0, 0, 1, 1, 0, 1, 1, 1, 0},
|
||||||
|
{0, 1, 1, 1, 1, 2, 0, 1, 0},
|
||||||
|
{1, 2, 2, 0, 2, 1, 1, 1, 0},
|
||||||
|
{1, 1, 1, 2, 0, 1, 1, 2, 0},
|
||||||
|
{0, 1, 1, 1, 2, 0, 1, 2, 0},
|
||||||
|
{0, 0, 0, 1, 1, 2, 1, 2, 0},
|
||||||
|
{1, 1, 1, 2, 0, 2, 1, 2, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
|
// INKPLATE_5_V2
|
||||||
|
{{0, 0, 1, 1, 2, 1, 1, 1, 0},
|
||||||
|
{1, 1, 2, 2, 1, 2, 1, 1, 0},
|
||||||
|
{0, 1, 2, 2, 1, 1, 2, 1, 0},
|
||||||
|
{0, 0, 1, 1, 1, 1, 1, 2, 0},
|
||||||
|
{1, 2, 1, 2, 1, 1, 1, 2, 0},
|
||||||
|
{0, 1, 1, 1, 2, 0, 1, 2, 0},
|
||||||
|
{1, 1, 1, 2, 2, 2, 1, 2, 0},
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 0}}};
|
||||||
|
|
||||||
void set_greyscale(bool greyscale) {
|
void set_greyscale(bool greyscale) {
|
||||||
this->greyscale_ = greyscale;
|
this->greyscale_ = greyscale;
|
||||||
|
@ -146,6 +166,10 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||||
return 800;
|
return 800;
|
||||||
} else if (this->model_ == INKPLATE_10) {
|
} else if (this->model_ == INKPLATE_10) {
|
||||||
return 1200;
|
return 1200;
|
||||||
|
} else if (this->model_ == INKPLATE_5) {
|
||||||
|
return 960;
|
||||||
|
} else if (this->model_ == INKPLATE_5_V2) {
|
||||||
|
return 1280;
|
||||||
} else if (this->model_ == INKPLATE_6_PLUS) {
|
} else if (this->model_ == INKPLATE_6_PLUS) {
|
||||||
return 1024;
|
return 1024;
|
||||||
}
|
}
|
||||||
|
@ -155,6 +179,10 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||||
int get_height_internal() override {
|
int get_height_internal() override {
|
||||||
if (this->model_ == INKPLATE_6 || this->model_ == INKPLATE_6_V2) {
|
if (this->model_ == INKPLATE_6 || this->model_ == INKPLATE_6_V2) {
|
||||||
return 600;
|
return 600;
|
||||||
|
} else if (this->model_ == INKPLATE_5) {
|
||||||
|
return 540;
|
||||||
|
} else if (this->model_ == INKPLATE_5_V2) {
|
||||||
|
return 720;
|
||||||
} else if (this->model_ == INKPLATE_10) {
|
} else if (this->model_ == INKPLATE_10) {
|
||||||
return 825;
|
return 825;
|
||||||
} else if (this->model_ == INKPLATE_6_PLUS) {
|
} else if (this->model_ == INKPLATE_6_PLUS) {
|
||||||
|
|
|
@ -22,9 +22,10 @@ from esphome.helpers import write_file_if_changed
|
||||||
|
|
||||||
from . import defines as df, helpers, lv_validation as lvalid
|
from . import defines as df, helpers, lv_validation as lvalid
|
||||||
from .automation import disp_update, focused_widgets, update_to_code
|
from .automation import disp_update, focused_widgets, update_to_code
|
||||||
from .defines import add_define
|
from .defines import CONF_WIDGETS, add_define
|
||||||
from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
|
from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
|
||||||
from .gradient import GRADIENT_SCHEMA, gradients_to_code
|
from .gradient import GRADIENT_SCHEMA, gradients_to_code
|
||||||
|
from .hello_world import get_hello_world
|
||||||
from .lv_validation import lv_bool, lv_images_used
|
from .lv_validation import lv_bool, lv_images_used
|
||||||
from .lvcode import LvContext, LvglComponent
|
from .lvcode import LvContext, LvglComponent
|
||||||
from .schemas import (
|
from .schemas import (
|
||||||
|
@ -32,7 +33,7 @@ from .schemas import (
|
||||||
FLEX_OBJ_SCHEMA,
|
FLEX_OBJ_SCHEMA,
|
||||||
GRID_CELL_SCHEMA,
|
GRID_CELL_SCHEMA,
|
||||||
LAYOUT_SCHEMAS,
|
LAYOUT_SCHEMAS,
|
||||||
STYLE_SCHEMA,
|
STATE_SCHEMA,
|
||||||
WIDGET_TYPES,
|
WIDGET_TYPES,
|
||||||
any_widget_schema,
|
any_widget_schema,
|
||||||
container_schema,
|
container_schema,
|
||||||
|
@ -292,6 +293,13 @@ def display_schema(config):
|
||||||
return value or [cv.use_id(Display)(config)]
|
return value or [cv.use_id(Display)(config)]
|
||||||
|
|
||||||
|
|
||||||
|
def add_hello_world(config):
|
||||||
|
if CONF_WIDGETS not in config and CONF_PAGES not in config:
|
||||||
|
LOGGER.info("No pages or widgets configured, creating default hello_world page")
|
||||||
|
config[CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = final_validation
|
FINAL_VALIDATE_SCHEMA = final_validation
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
|
@ -313,7 +321,7 @@ CONFIG_SCHEMA = (
|
||||||
),
|
),
|
||||||
cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list(
|
cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list(
|
||||||
cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)})
|
cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)})
|
||||||
.extend(STYLE_SCHEMA)
|
.extend(STATE_SCHEMA)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments,
|
cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments,
|
||||||
|
@ -349,4 +357,5 @@ CONFIG_SCHEMA = (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(DISP_BG_SCHEMA)
|
.extend(DISP_BG_SCHEMA)
|
||||||
).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS))
|
.add_extra(add_hello_world)
|
||||||
|
)
|
||||||
|
|
64
esphome/components/lvgl/hello_world.py
Normal file
64
esphome/components/lvgl/hello_world.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from esphome.yaml_util import parse_yaml
|
||||||
|
|
||||||
|
CONFIG = """
|
||||||
|
- obj:
|
||||||
|
radius: 0
|
||||||
|
pad_all: 12
|
||||||
|
bg_color: 0xFFFFFF
|
||||||
|
height: 100%
|
||||||
|
width: 100%
|
||||||
|
widgets:
|
||||||
|
- spinner:
|
||||||
|
id: hello_world_spinner_
|
||||||
|
align: center
|
||||||
|
indicator:
|
||||||
|
arc_color: tomato
|
||||||
|
height: 100
|
||||||
|
width: 100
|
||||||
|
spin_time: 2s
|
||||||
|
arc_length: 60deg
|
||||||
|
- label:
|
||||||
|
id: hello_world_label_
|
||||||
|
text: "Hello World!"
|
||||||
|
align: center
|
||||||
|
on_click:
|
||||||
|
lvgl.spinner.update:
|
||||||
|
id: hello_world_spinner_
|
||||||
|
arc_color: springgreen
|
||||||
|
- checkbox:
|
||||||
|
pad_all: 8
|
||||||
|
text: Checkbox
|
||||||
|
align: top_right
|
||||||
|
on_click:
|
||||||
|
lvgl.label.update:
|
||||||
|
id: hello_world_label_
|
||||||
|
text: "Checked!"
|
||||||
|
- button:
|
||||||
|
pad_all: 8
|
||||||
|
checkable: true
|
||||||
|
align: top_left
|
||||||
|
text_font: montserrat_20
|
||||||
|
on_click:
|
||||||
|
lvgl.label.update:
|
||||||
|
id: hello_world_label_
|
||||||
|
text: "Clicked!"
|
||||||
|
widgets:
|
||||||
|
- label:
|
||||||
|
text: "Button"
|
||||||
|
- slider:
|
||||||
|
width: 80%
|
||||||
|
align: bottom_mid
|
||||||
|
on_value:
|
||||||
|
lvgl.label.update:
|
||||||
|
id: hello_world_label_
|
||||||
|
text:
|
||||||
|
format: "%.0f%%"
|
||||||
|
args: [x]
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_hello_world():
|
||||||
|
with StringIO(CONFIG) as fp:
|
||||||
|
return parse_yaml("hello_world", fp)
|
|
@ -49,17 +49,172 @@ def opacity_validator(value):
|
||||||
|
|
||||||
opacity = LValidator(opacity_validator, uint32, retmapper=literal)
|
opacity = LValidator(opacity_validator, uint32, retmapper=literal)
|
||||||
|
|
||||||
|
COLOR_NAMES = {
|
||||||
|
"aliceblue": 0xF0F8FF,
|
||||||
|
"antiquewhite": 0xFAEBD7,
|
||||||
|
"aqua": 0x00FFFF,
|
||||||
|
"aquamarine": 0x7FFFD4,
|
||||||
|
"azure": 0xF0FFFF,
|
||||||
|
"beige": 0xF5F5DC,
|
||||||
|
"bisque": 0xFFE4C4,
|
||||||
|
"black": 0x000000,
|
||||||
|
"blanchedalmond": 0xFFEBCD,
|
||||||
|
"blue": 0x0000FF,
|
||||||
|
"blueviolet": 0x8A2BE2,
|
||||||
|
"brown": 0xA52A2A,
|
||||||
|
"burlywood": 0xDEB887,
|
||||||
|
"cadetblue": 0x5F9EA0,
|
||||||
|
"chartreuse": 0x7FFF00,
|
||||||
|
"chocolate": 0xD2691E,
|
||||||
|
"coral": 0xFF7F50,
|
||||||
|
"cornflowerblue": 0x6495ED,
|
||||||
|
"cornsilk": 0xFFF8DC,
|
||||||
|
"crimson": 0xDC143C,
|
||||||
|
"cyan": 0x00FFFF,
|
||||||
|
"darkblue": 0x00008B,
|
||||||
|
"darkcyan": 0x008B8B,
|
||||||
|
"darkgoldenrod": 0xB8860B,
|
||||||
|
"darkgray": 0xA9A9A9,
|
||||||
|
"darkgreen": 0x006400,
|
||||||
|
"darkgrey": 0xA9A9A9,
|
||||||
|
"darkkhaki": 0xBDB76B,
|
||||||
|
"darkmagenta": 0x8B008B,
|
||||||
|
"darkolivegreen": 0x556B2F,
|
||||||
|
"darkorange": 0xFF8C00,
|
||||||
|
"darkorchid": 0x9932CC,
|
||||||
|
"darkred": 0x8B0000,
|
||||||
|
"darksalmon": 0xE9967A,
|
||||||
|
"darkseagreen": 0x8FBC8F,
|
||||||
|
"darkslateblue": 0x483D8B,
|
||||||
|
"darkslategray": 0x2F4F4F,
|
||||||
|
"darkslategrey": 0x2F4F4F,
|
||||||
|
"darkturquoise": 0x00CED1,
|
||||||
|
"darkviolet": 0x9400D3,
|
||||||
|
"deeppink": 0xFF1493,
|
||||||
|
"deepskyblue": 0x00BFFF,
|
||||||
|
"dimgray": 0x696969,
|
||||||
|
"dimgrey": 0x696969,
|
||||||
|
"dodgerblue": 0x1E90FF,
|
||||||
|
"firebrick": 0xB22222,
|
||||||
|
"floralwhite": 0xFFFAF0,
|
||||||
|
"forestgreen": 0x228B22,
|
||||||
|
"fuchsia": 0xFF00FF,
|
||||||
|
"gainsboro": 0xDCDCDC,
|
||||||
|
"ghostwhite": 0xF8F8FF,
|
||||||
|
"goldenrod": 0xDAA520,
|
||||||
|
"gold": 0xFFD700,
|
||||||
|
"gray": 0x808080,
|
||||||
|
"green": 0x008000,
|
||||||
|
"greenyellow": 0xADFF2F,
|
||||||
|
"grey": 0x808080,
|
||||||
|
"honeydew": 0xF0FFF0,
|
||||||
|
"hotpink": 0xFF69B4,
|
||||||
|
"indianred": 0xCD5C5C,
|
||||||
|
"indigo": 0x4B0082,
|
||||||
|
"ivory": 0xFFFFF0,
|
||||||
|
"khaki": 0xF0E68C,
|
||||||
|
"lavenderblush": 0xFFF0F5,
|
||||||
|
"lavender": 0xE6E6FA,
|
||||||
|
"lawngreen": 0x7CFC00,
|
||||||
|
"lemonchiffon": 0xFFFACD,
|
||||||
|
"lightblue": 0xADD8E6,
|
||||||
|
"lightcoral": 0xF08080,
|
||||||
|
"lightcyan": 0xE0FFFF,
|
||||||
|
"lightgoldenrodyellow": 0xFAFAD2,
|
||||||
|
"lightgray": 0xD3D3D3,
|
||||||
|
"lightgreen": 0x90EE90,
|
||||||
|
"lightgrey": 0xD3D3D3,
|
||||||
|
"lightpink": 0xFFB6C1,
|
||||||
|
"lightsalmon": 0xFFA07A,
|
||||||
|
"lightseagreen": 0x20B2AA,
|
||||||
|
"lightskyblue": 0x87CEFA,
|
||||||
|
"lightslategray": 0x778899,
|
||||||
|
"lightslategrey": 0x778899,
|
||||||
|
"lightsteelblue": 0xB0C4DE,
|
||||||
|
"lightyellow": 0xFFFFE0,
|
||||||
|
"lime": 0x00FF00,
|
||||||
|
"limegreen": 0x32CD32,
|
||||||
|
"linen": 0xFAF0E6,
|
||||||
|
"magenta": 0xFF00FF,
|
||||||
|
"maroon": 0x800000,
|
||||||
|
"mediumaquamarine": 0x66CDAA,
|
||||||
|
"mediumblue": 0x0000CD,
|
||||||
|
"mediumorchid": 0xBA55D3,
|
||||||
|
"mediumpurple": 0x9370DB,
|
||||||
|
"mediumseagreen": 0x3CB371,
|
||||||
|
"mediumslateblue": 0x7B68EE,
|
||||||
|
"mediumspringgreen": 0x00FA9A,
|
||||||
|
"mediumturquoise": 0x48D1CC,
|
||||||
|
"mediumvioletred": 0xC71585,
|
||||||
|
"midnightblue": 0x191970,
|
||||||
|
"mintcream": 0xF5FFFA,
|
||||||
|
"mistyrose": 0xFFE4E1,
|
||||||
|
"moccasin": 0xFFE4B5,
|
||||||
|
"navajowhite": 0xFFDEAD,
|
||||||
|
"navy": 0x000080,
|
||||||
|
"oldlace": 0xFDF5E6,
|
||||||
|
"olive": 0x808000,
|
||||||
|
"olivedrab": 0x6B8E23,
|
||||||
|
"orange": 0xFFA500,
|
||||||
|
"orangered": 0xFF4500,
|
||||||
|
"orchid": 0xDA70D6,
|
||||||
|
"palegoldenrod": 0xEEE8AA,
|
||||||
|
"palegreen": 0x98FB98,
|
||||||
|
"paleturquoise": 0xAFEEEE,
|
||||||
|
"palevioletred": 0xDB7093,
|
||||||
|
"papayawhip": 0xFFEFD5,
|
||||||
|
"peachpuff": 0xFFDAB9,
|
||||||
|
"peru": 0xCD853F,
|
||||||
|
"pink": 0xFFC0CB,
|
||||||
|
"plum": 0xDDA0DD,
|
||||||
|
"powderblue": 0xB0E0E6,
|
||||||
|
"purple": 0x800080,
|
||||||
|
"rebeccapurple": 0x663399,
|
||||||
|
"red": 0xFF0000,
|
||||||
|
"rosybrown": 0xBC8F8F,
|
||||||
|
"royalblue": 0x4169E1,
|
||||||
|
"saddlebrown": 0x8B4513,
|
||||||
|
"salmon": 0xFA8072,
|
||||||
|
"sandybrown": 0xF4A460,
|
||||||
|
"seagreen": 0x2E8B57,
|
||||||
|
"seashell": 0xFFF5EE,
|
||||||
|
"sienna": 0xA0522D,
|
||||||
|
"silver": 0xC0C0C0,
|
||||||
|
"skyblue": 0x87CEEB,
|
||||||
|
"slateblue": 0x6A5ACD,
|
||||||
|
"slategray": 0x708090,
|
||||||
|
"slategrey": 0x708090,
|
||||||
|
"snow": 0xFFFAFA,
|
||||||
|
"springgreen": 0x00FF7F,
|
||||||
|
"steelblue": 0x4682B4,
|
||||||
|
"tan": 0xD2B48C,
|
||||||
|
"teal": 0x008080,
|
||||||
|
"thistle": 0xD8BFD8,
|
||||||
|
"tomato": 0xFF6347,
|
||||||
|
"turquoise": 0x40E0D0,
|
||||||
|
"violet": 0xEE82EE,
|
||||||
|
"wheat": 0xF5DEB3,
|
||||||
|
"white": 0xFFFFFF,
|
||||||
|
"whitesmoke": 0xF5F5F5,
|
||||||
|
"yellow": 0xFFFF00,
|
||||||
|
"yellowgreen": 0x9ACD32,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@schema_extractor("one_of")
|
@schema_extractor("one_of")
|
||||||
def color(value):
|
def color(value):
|
||||||
if value == SCHEMA_EXTRACT:
|
if value == SCHEMA_EXTRACT:
|
||||||
return ["hex color value", "color ID"]
|
return ["hex color value", "color ID"]
|
||||||
return cv.Any(cv.int_, cv.use_id(ColorStruct))(value)
|
return cv.Any(cv.int_, cv.one_of(*COLOR_NAMES, lower=True), cv.use_id(ColorStruct))(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def color_retmapper(value):
|
def color_retmapper(value):
|
||||||
if isinstance(value, cv.Lambda):
|
if isinstance(value, cv.Lambda):
|
||||||
return cv.returning_lambda(value)
|
return cv.returning_lambda(value)
|
||||||
|
if isinstance(value, str) and value in COLOR_NAMES:
|
||||||
|
value = COLOR_NAMES[value]
|
||||||
if isinstance(value, int):
|
if isinstance(value, int):
|
||||||
return literal(
|
return literal(
|
||||||
f"lv_color_make({(value >> 16) & 0xFF}, {(value >> 8) & 0xFF}, {value & 0xFF})"
|
f"lv_color_make({(value >> 16) & 0xFF}, {(value >> 8) & 0xFF}, {value & 0xFF})"
|
||||||
|
|
|
@ -419,6 +419,13 @@ async def to_code(config):
|
||||||
repo="https://github.com/espressif/esp-tflite-micro",
|
repo="https://github.com/espressif/esp-tflite-micro",
|
||||||
ref="v1.3.1",
|
ref="v1.3.1",
|
||||||
)
|
)
|
||||||
|
# add esp-nn dependency for tflite-micro to work around https://github.com/espressif/esp-nn/issues/17
|
||||||
|
# ...remove after switching to IDF 5.1.4+
|
||||||
|
esp32.add_idf_component(
|
||||||
|
name="esp-nn",
|
||||||
|
repo="https://github.com/espressif/esp-nn",
|
||||||
|
ref="v1.1.0",
|
||||||
|
)
|
||||||
|
|
||||||
cg.add_build_flag("-DTF_LITE_STATIC_MEMORY")
|
cg.add_build_flag("-DTF_LITE_STATIC_MEMORY")
|
||||||
cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON")
|
cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON")
|
||||||
|
|
|
@ -11,6 +11,7 @@ from esphome.const import (
|
||||||
CONF_BIRTH_MESSAGE,
|
CONF_BIRTH_MESSAGE,
|
||||||
CONF_BROKER,
|
CONF_BROKER,
|
||||||
CONF_CERTIFICATE_AUTHORITY,
|
CONF_CERTIFICATE_AUTHORITY,
|
||||||
|
CONF_CLEAN_SESSION,
|
||||||
CONF_CLIENT_CERTIFICATE,
|
CONF_CLIENT_CERTIFICATE,
|
||||||
CONF_CLIENT_CERTIFICATE_KEY,
|
CONF_CLIENT_CERTIFICATE_KEY,
|
||||||
CONF_CLIENT_ID,
|
CONF_CLIENT_ID,
|
||||||
|
@ -209,6 +210,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_PORT, default=1883): cv.port,
|
cv.Optional(CONF_PORT, default=1883): cv.port,
|
||||||
cv.Optional(CONF_USERNAME, default=""): cv.string,
|
cv.Optional(CONF_USERNAME, default=""): cv.string,
|
||||||
cv.Optional(CONF_PASSWORD, default=""): cv.string,
|
cv.Optional(CONF_PASSWORD, default=""): cv.string,
|
||||||
|
cv.Optional(CONF_CLEAN_SESSION, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_CLIENT_ID): cv.string,
|
cv.Optional(CONF_CLIENT_ID): cv.string,
|
||||||
cv.SplitDefault(CONF_IDF_SEND_ASYNC, esp32_idf=False): cv.All(
|
cv.SplitDefault(CONF_IDF_SEND_ASYNC, esp32_idf=False): cv.All(
|
||||||
cv.boolean, cv.only_with_esp_idf
|
cv.boolean, cv.only_with_esp_idf
|
||||||
|
@ -325,6 +327,7 @@ async def to_code(config):
|
||||||
cg.add(var.set_broker_port(config[CONF_PORT]))
|
cg.add(var.set_broker_port(config[CONF_PORT]))
|
||||||
cg.add(var.set_username(config[CONF_USERNAME]))
|
cg.add(var.set_username(config[CONF_USERNAME]))
|
||||||
cg.add(var.set_password(config[CONF_PASSWORD]))
|
cg.add(var.set_password(config[CONF_PASSWORD]))
|
||||||
|
cg.add(var.set_clean_session(config[CONF_CLEAN_SESSION]))
|
||||||
if CONF_CLIENT_ID in config:
|
if CONF_CLIENT_ID in config:
|
||||||
cg.add(var.set_client_id(config[CONF_CLIENT_ID]))
|
cg.add(var.set_client_id(config[CONF_CLIENT_ID]))
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,7 @@ void MQTTClientComponent::dump_config() {
|
||||||
this->ip_.str().c_str());
|
this->ip_.str().c_str());
|
||||||
ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str());
|
ESP_LOGCONFIG(TAG, " Username: " LOG_SECRET("'%s'"), this->credentials_.username.c_str());
|
||||||
ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str());
|
ESP_LOGCONFIG(TAG, " Client ID: " LOG_SECRET("'%s'"), this->credentials_.client_id.c_str());
|
||||||
|
ESP_LOGCONFIG(TAG, " Clean Session: %s", YESNO(this->credentials_.clean_session));
|
||||||
if (this->is_discovery_ip_enabled()) {
|
if (this->is_discovery_ip_enabled()) {
|
||||||
ESP_LOGCONFIG(TAG, " Discovery IP enabled");
|
ESP_LOGCONFIG(TAG, " Discovery IP enabled");
|
||||||
}
|
}
|
||||||
|
@ -246,6 +247,7 @@ void MQTTClientComponent::start_connect_() {
|
||||||
this->mqtt_backend_.disconnect();
|
this->mqtt_backend_.disconnect();
|
||||||
|
|
||||||
this->mqtt_backend_.set_client_id(this->credentials_.client_id.c_str());
|
this->mqtt_backend_.set_client_id(this->credentials_.client_id.c_str());
|
||||||
|
this->mqtt_backend_.set_clean_session(this->credentials_.clean_session);
|
||||||
const char *username = nullptr;
|
const char *username = nullptr;
|
||||||
if (!this->credentials_.username.empty())
|
if (!this->credentials_.username.empty())
|
||||||
username = this->credentials_.username.c_str();
|
username = this->credentials_.username.c_str();
|
||||||
|
|
|
@ -51,6 +51,7 @@ struct MQTTCredentials {
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string password;
|
std::string password;
|
||||||
std::string client_id; ///< The client ID. Will automatically be truncated to 23 characters.
|
std::string client_id; ///< The client ID. Will automatically be truncated to 23 characters.
|
||||||
|
bool clean_session; ///< Whether the session will be cleaned or remembered between connects.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Simple data struct for Home Assistant component availability.
|
/// Simple data struct for Home Assistant component availability.
|
||||||
|
@ -254,6 +255,7 @@ class MQTTClientComponent : public Component {
|
||||||
void set_username(const std::string &username) { this->credentials_.username = username; }
|
void set_username(const std::string &username) { this->credentials_.username = username; }
|
||||||
void set_password(const std::string &password) { this->credentials_.password = password; }
|
void set_password(const std::string &password) { this->credentials_.password = password; }
|
||||||
void set_client_id(const std::string &client_id) { this->credentials_.client_id = client_id; }
|
void set_client_id(const std::string &client_id) { this->credentials_.client_id = client_id; }
|
||||||
|
void set_clean_session(const bool &clean_session) { this->credentials_.clean_session = clean_session; }
|
||||||
void set_on_connect(mqtt_on_connect_callback_t &&callback);
|
void set_on_connect(mqtt_on_connect_callback_t &&callback);
|
||||||
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback);
|
void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
|
||||||
CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
|
CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
|
||||||
CONF_FONT_ID = "font_id"
|
CONF_FONT_ID = "font_id"
|
||||||
CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start"
|
CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start"
|
||||||
|
CONF_SKIP_CONNECTION_HANDSHAKE = "skip_connection_handshake"
|
||||||
|
|
||||||
|
|
||||||
def NextionName(value):
|
def NextionName(value):
|
||||||
|
|
|
@ -23,6 +23,7 @@ from .base_component import (
|
||||||
CONF_START_UP_PAGE,
|
CONF_START_UP_PAGE,
|
||||||
CONF_AUTO_WAKE_ON_TOUCH,
|
CONF_AUTO_WAKE_ON_TOUCH,
|
||||||
CONF_EXIT_REPARSE_ON_START,
|
CONF_EXIT_REPARSE_ON_START,
|
||||||
|
CONF_SKIP_CONNECTION_HANDSHAKE,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"]
|
CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"]
|
||||||
|
@ -72,6 +73,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(CONF_START_UP_PAGE): cv.uint8_t,
|
cv.Optional(CONF_START_UP_PAGE): cv.uint8_t,
|
||||||
cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean,
|
cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean,
|
cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("5s"))
|
.extend(cv.polling_component_schema("5s"))
|
||||||
|
@ -118,6 +120,8 @@ async def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_exit_reparse_on_start_internal(config[CONF_EXIT_REPARSE_ON_START]))
|
cg.add(var.set_exit_reparse_on_start_internal(config[CONF_EXIT_REPARSE_ON_START]))
|
||||||
|
|
||||||
|
cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE]))
|
||||||
|
|
||||||
await display.register_display(var, config)
|
await display.register_display(var, config)
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_SETUP, []):
|
for conf in config.get(CONF_ON_SETUP, []):
|
||||||
|
|
|
@ -43,6 +43,16 @@ bool Nextion::check_connect_() {
|
||||||
if (this->get_is_connected_())
|
if (this->get_is_connected_())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Check if the handshake should be skipped for the Nextion connection
|
||||||
|
if (this->skip_connection_handshake_) {
|
||||||
|
// Log the connection status without handshake
|
||||||
|
ESP_LOGW(TAG, "Nextion display set as connected without performing handshake");
|
||||||
|
// Set the connection status to true
|
||||||
|
this->is_connected_ = true;
|
||||||
|
// Return true indicating the connection is set
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->comok_sent_ == 0) {
|
if (this->comok_sent_ == 0) {
|
||||||
this->reset_(false);
|
this->reset_(false);
|
||||||
|
|
||||||
|
@ -126,10 +136,14 @@ void Nextion::reset_(bool reset_nextion) {
|
||||||
|
|
||||||
void Nextion::dump_config() {
|
void Nextion::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Nextion:");
|
ESP_LOGCONFIG(TAG, "Nextion:");
|
||||||
|
if (this->skip_connection_handshake_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Skip handshake: %s", YESNO(this->skip_connection_handshake_));
|
||||||
|
} else {
|
||||||
ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str());
|
ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str());
|
||||||
ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str());
|
ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str());
|
||||||
ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str());
|
ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str());
|
||||||
ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str());
|
ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str());
|
||||||
|
}
|
||||||
ESP_LOGCONFIG(TAG, " Wake On Touch: %s", YESNO(this->auto_wake_on_touch_));
|
ESP_LOGCONFIG(TAG, " Wake On Touch: %s", YESNO(this->auto_wake_on_touch_));
|
||||||
ESP_LOGCONFIG(TAG, " Exit reparse: %s", YESNO(this->exit_reparse_on_start_));
|
ESP_LOGCONFIG(TAG, " Exit reparse: %s", YESNO(this->exit_reparse_on_start_));
|
||||||
|
|
||||||
|
@ -262,6 +276,7 @@ void Nextion::loop() {
|
||||||
this->goto_page(this->start_up_page_);
|
this->goto_page(this->start_up_page_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This could probably be removed from the loop area, as those are redundant.
|
||||||
this->set_auto_wake_on_touch(this->auto_wake_on_touch_);
|
this->set_auto_wake_on_touch(this->auto_wake_on_touch_);
|
||||||
this->set_exit_reparse_on_start(this->exit_reparse_on_start_);
|
this->set_exit_reparse_on_start(this->exit_reparse_on_start_);
|
||||||
|
|
||||||
|
|
|
@ -926,6 +926,21 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
*/
|
*/
|
||||||
void set_exit_reparse_on_start(bool exit_reparse);
|
void set_exit_reparse_on_start(bool exit_reparse);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the Nextion display should skip the connection handshake process.
|
||||||
|
* @param skip_handshake True or false. When skip_connection_handshake is true,
|
||||||
|
* the connection will be established without performing the handshake.
|
||||||
|
* This can be useful when using Nextion Simulator.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```cpp
|
||||||
|
* it.set_skip_connection_handshake(true);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* When set to true, the display will be marked as connected without performing a handshake.
|
||||||
|
*/
|
||||||
|
void set_skip_connection_handshake(bool skip_handshake) { this->skip_connection_handshake_ = skip_handshake; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets Nextion mode between sleep and awake
|
* Sets Nextion mode between sleep and awake
|
||||||
* @param True or false. Sleep=true to enter sleep mode or sleep=false to exit sleep mode.
|
* @param True or false. Sleep=true to enter sleep mode or sleep=false to exit sleep mode.
|
||||||
|
@ -1221,6 +1236,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
int16_t start_up_page_ = -1;
|
int16_t start_up_page_ = -1;
|
||||||
bool auto_wake_on_touch_ = true;
|
bool auto_wake_on_touch_ = true;
|
||||||
bool exit_reparse_on_start_ = false;
|
bool exit_reparse_on_start_ = false;
|
||||||
|
bool skip_connection_handshake_ = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually send a raw command to the display and don't wait for an acknowledgement packet.
|
* Manually send a raw command to the display and don't wait for an acknowledgement packet.
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import esp32_rmt, remote_base
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
|
||||||
from esphome.components import remote_base, esp32_rmt
|
|
||||||
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL
|
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL
|
||||||
|
|
||||||
AUTO_LOAD = ["remote_base"]
|
AUTO_LOAD = ["remote_base"]
|
||||||
|
|
||||||
|
CONF_ON_TRANSMIT = "on_transmit"
|
||||||
|
CONF_ON_COMPLETE = "on_complete"
|
||||||
|
|
||||||
remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter")
|
remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter")
|
||||||
RemoteTransmitterComponent = remote_transmitter_ns.class_(
|
RemoteTransmitterComponent = remote_transmitter_ns.class_(
|
||||||
"RemoteTransmitterComponent", remote_base.RemoteTransmitterBase, cg.Component
|
"RemoteTransmitterComponent", remote_base.RemoteTransmitterBase, cg.Component
|
||||||
|
@ -19,6 +23,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.percentage_int, cv.Range(min=1, max=100)
|
cv.percentage_int, cv.Range(min=1, max=100)
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True),
|
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True),
|
||||||
|
cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True),
|
||||||
|
cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
@ -32,3 +38,13 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT]))
|
cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT]))
|
||||||
|
|
||||||
|
if on_transmit_config := config.get(CONF_ON_TRANSMIT):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_transmit_trigger(), [], on_transmit_config
|
||||||
|
)
|
||||||
|
|
||||||
|
if on_complete_config := config.get(CONF_ON_COMPLETE):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_complete_trigger(), [], on_complete_config
|
||||||
|
)
|
||||||
|
|
|
@ -33,6 +33,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||||
|
|
||||||
void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; }
|
void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; }
|
||||||
|
|
||||||
|
Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; };
|
||||||
|
Trigger<> *get_complete_trigger() const { return this->complete_trigger_; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void send_internal(uint32_t send_times, uint32_t send_wait) override;
|
void send_internal(uint32_t send_times, uint32_t send_wait) override;
|
||||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
||||||
|
@ -57,6 +60,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||||
bool inverted_{false};
|
bool inverted_{false};
|
||||||
#endif
|
#endif
|
||||||
uint8_t carrier_duty_percent_;
|
uint8_t carrier_duty_percent_;
|
||||||
|
|
||||||
|
Trigger<> *transmit_trigger_{new Trigger<>()};
|
||||||
|
Trigger<> *complete_trigger_{new Trigger<>()};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace remote_transmitter
|
||||||
|
|
|
@ -124,6 +124,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||||
ESP_LOGE(TAG, "Empty data");
|
ESP_LOGE(TAG, "Empty data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->transmit_trigger_->trigger();
|
||||||
for (uint32_t i = 0; i < send_times; i++) {
|
for (uint32_t i = 0; i < send_times; i++) {
|
||||||
esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true);
|
esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true);
|
||||||
if (error != ESP_OK) {
|
if (error != ESP_OK) {
|
||||||
|
@ -135,6 +136,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||||
if (i + 1 < send_times)
|
if (i + 1 < send_times)
|
||||||
delayMicroseconds(send_wait);
|
delayMicroseconds(send_wait);
|
||||||
}
|
}
|
||||||
|
this->complete_trigger_->trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace remote_transmitter
|
||||||
|
|
|
@ -76,6 +76,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||||
uint32_t on_time, off_time;
|
uint32_t on_time, off_time;
|
||||||
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
|
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
|
||||||
this->target_time_ = 0;
|
this->target_time_ = 0;
|
||||||
|
this->transmit_trigger_->trigger();
|
||||||
for (uint32_t i = 0; i < send_times; i++) {
|
for (uint32_t i = 0; i < send_times; i++) {
|
||||||
for (int32_t item : this->temp_.get_data()) {
|
for (int32_t item : this->temp_.get_data()) {
|
||||||
if (item > 0) {
|
if (item > 0) {
|
||||||
|
@ -93,6 +94,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||||
if (i + 1 < send_times)
|
if (i + 1 < send_times)
|
||||||
this->target_time_ += send_wait;
|
this->target_time_ += send_wait;
|
||||||
}
|
}
|
||||||
|
this->complete_trigger_->trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace remote_transmitter
|
||||||
|
|
|
@ -78,6 +78,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||||
uint32_t on_time, off_time;
|
uint32_t on_time, off_time;
|
||||||
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
|
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
|
||||||
this->target_time_ = 0;
|
this->target_time_ = 0;
|
||||||
|
this->transmit_trigger_->trigger();
|
||||||
for (uint32_t i = 0; i < send_times; i++) {
|
for (uint32_t i = 0; i < send_times; i++) {
|
||||||
InterruptLock lock;
|
InterruptLock lock;
|
||||||
for (int32_t item : this->temp_.get_data()) {
|
for (int32_t item : this->temp_.get_data()) {
|
||||||
|
@ -96,6 +97,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
|
||||||
if (i + 1 < send_times)
|
if (i + 1 < send_times)
|
||||||
this->target_time_ += send_wait;
|
this->target_time_ += send_wait;
|
||||||
}
|
}
|
||||||
|
this->complete_trigger_->trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remote_transmitter
|
} // namespace remote_transmitter
|
||||||
|
|
|
@ -9,14 +9,6 @@ void ST7701S::setup() {
|
||||||
esph_log_config(TAG, "Setting up ST7701S");
|
esph_log_config(TAG, "Setting up ST7701S");
|
||||||
this->spi_setup();
|
this->spi_setup();
|
||||||
this->write_init_sequence_();
|
this->write_init_sequence_();
|
||||||
}
|
|
||||||
|
|
||||||
// called after a delay after writing the init sequence
|
|
||||||
void ST7701S::complete_setup_() {
|
|
||||||
this->write_command_(SLEEP_OUT);
|
|
||||||
this->write_command_(DISPLAY_ON);
|
|
||||||
this->spi_teardown(); // SPI not needed after this
|
|
||||||
delay(10);
|
|
||||||
|
|
||||||
esp_lcd_rgb_panel_config_t config{};
|
esp_lcd_rgb_panel_config_t config{};
|
||||||
config.flags.fb_in_psram = 1;
|
config.flags.fb_in_psram = 1;
|
||||||
|
@ -179,7 +171,12 @@ void ST7701S::write_init_sequence_() {
|
||||||
this->write_data_(val);
|
this->write_data_(val);
|
||||||
ESP_LOGD(TAG, "write MADCTL %X", val);
|
ESP_LOGD(TAG, "write MADCTL %X", val);
|
||||||
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
|
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
|
||||||
this->set_timeout(120, [this] { this->complete_setup_(); });
|
// can't avoid this inline delay due to the need to complete setup before anything else tries to draw.
|
||||||
|
delay(120); // NOLINT
|
||||||
|
this->write_command_(SLEEP_OUT);
|
||||||
|
this->write_command_(DISPLAY_ON);
|
||||||
|
this->spi_teardown(); // SPI not needed after this
|
||||||
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ST7701S::dump_config() {
|
void ST7701S::dump_config() {
|
||||||
|
|
72
esphome/components/tca9555/__init__.py
Normal file
72
esphome/components/tca9555/__init__.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_INVERTED,
|
||||||
|
CONF_MODE,
|
||||||
|
CONF_NUMBER,
|
||||||
|
CONF_OUTPUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@mobrembski"]
|
||||||
|
|
||||||
|
AUTO_LOAD = ["gpio_expander"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
tca9555_ns = cg.esphome_ns.namespace("tca9555")
|
||||||
|
|
||||||
|
TCA9555Component = tca9555_ns.class_("TCA9555Component", cg.Component, i2c.I2CDevice)
|
||||||
|
TCA9555GPIOPin = tca9555_ns.class_("TCA9555GPIOPin", cg.GPIOPin)
|
||||||
|
|
||||||
|
CONF_TCA9555 = "tca9555"
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.declare_id(TCA9555Component),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(i2c.i2c_device_schema(0x21))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_mode(value):
|
||||||
|
if not (value[CONF_INPUT] or value[CONF_OUTPUT]):
|
||||||
|
raise cv.Invalid("Mode must be either input or output")
|
||||||
|
if value[CONF_INPUT] and value[CONF_OUTPUT]:
|
||||||
|
raise cv.Invalid("Mode must be either input or output")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
TCA9555_PIN_SCHEMA = pins.gpio_base_schema(
|
||||||
|
TCA9555GPIOPin,
|
||||||
|
cv.int_range(min=0, max=15),
|
||||||
|
modes=[CONF_INPUT, CONF_OUTPUT],
|
||||||
|
mode_validator=validate_mode,
|
||||||
|
invertable=True,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_TCA9555): cv.use_id(TCA9555Component),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pins.PIN_SCHEMA_REGISTRY.register(CONF_TCA9555, TCA9555_PIN_SCHEMA)
|
||||||
|
async def tca9555_pin_to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_parented(var, config[CONF_TCA9555])
|
||||||
|
|
||||||
|
cg.add(var.set_pin(config[CONF_NUMBER]))
|
||||||
|
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||||
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
|
return var
|
140
esphome/components/tca9555/tca9555.cpp
Normal file
140
esphome/components/tca9555/tca9555.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#include "tca9555.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
static const uint8_t TCA9555_INPUT_PORT_REGISTER_0 = 0x00;
|
||||||
|
static const uint8_t TCA9555_INPUT_PORT_REGISTER_1 = 0x01;
|
||||||
|
static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_0 = 0x02;
|
||||||
|
static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_1 = 0x03;
|
||||||
|
static const uint8_t TCA9555_POLARITY_REGISTER_0 = 0x04;
|
||||||
|
static const uint8_t TCA9555_POLARITY_REGISTER_1 = 0x05;
|
||||||
|
static const uint8_t TCA9555_CONFIGURATION_PORT_0 = 0x06;
|
||||||
|
static const uint8_t TCA9555_CONFIGURATION_PORT_1 = 0x07;
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace tca9555 {
|
||||||
|
|
||||||
|
static const char *const TAG = "tca9555";
|
||||||
|
|
||||||
|
void TCA9555Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up TCA9555...");
|
||||||
|
if (!this->read_gpio_modes_()) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->read_gpio_outputs_()) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TCA9555Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "TCA9555:");
|
||||||
|
LOG_I2C_DEVICE(this)
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with TCA9555 failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TCA9555Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||||
|
if (flags == gpio::FLAG_INPUT) {
|
||||||
|
// Set mode mask bit
|
||||||
|
this->mode_mask_ |= 1 << pin;
|
||||||
|
} else if (flags == gpio::FLAG_OUTPUT) {
|
||||||
|
// Clear mode mask bit
|
||||||
|
this->mode_mask_ &= ~(1 << pin);
|
||||||
|
}
|
||||||
|
// Write GPIO to enable input mode
|
||||||
|
this->write_gpio_modes_();
|
||||||
|
}
|
||||||
|
void TCA9555Component::loop() { this->reset_pin_cache_(); }
|
||||||
|
|
||||||
|
bool TCA9555Component::read_gpio_outputs_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
uint8_t data[2];
|
||||||
|
if (!this->read_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
|
||||||
|
this->status_set_warning("Failed to read output register");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TCA9555Component::read_gpio_modes_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
uint8_t data[2];
|
||||||
|
bool success = this->read_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2);
|
||||||
|
if (!success) {
|
||||||
|
this->status_set_warning("Failed to read mode register");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool TCA9555Component::digital_read_hw(uint8_t pin) {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
bool success;
|
||||||
|
uint8_t data[2];
|
||||||
|
success = this->read_bytes(TCA9555_INPUT_PORT_REGISTER_0, data, 2);
|
||||||
|
this->input_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
this->status_set_warning("Failed to read input register");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCA9555Component::digital_write_hw(uint8_t pin, bool value) {
|
||||||
|
if (this->is_failed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
this->output_mask_ |= (1 << pin);
|
||||||
|
} else {
|
||||||
|
this->output_mask_ &= ~(1 << pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data[2];
|
||||||
|
data[0] = this->output_mask_;
|
||||||
|
data[1] = this->output_mask_ >> 8;
|
||||||
|
if (!this->write_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) {
|
||||||
|
this->status_set_warning("Failed to write output register");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TCA9555Component::write_gpio_modes_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
data[0] = this->mode_mask_;
|
||||||
|
data[1] = this->mode_mask_ >> 8;
|
||||||
|
if (!this->write_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2)) {
|
||||||
|
this->status_set_warning("Failed to write mode register");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TCA9555Component::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
|
||||||
|
|
||||||
|
float TCA9555Component::get_setup_priority() const { return setup_priority::IO; }
|
||||||
|
|
||||||
|
void TCA9555GPIOPin::setup() { this->pin_mode(this->flags_); }
|
||||||
|
void TCA9555GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||||
|
bool TCA9555GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||||
|
void TCA9555GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||||
|
std::string TCA9555GPIOPin::dump_summary() const { return str_sprintf("%u via TCA9555", this->pin_); }
|
||||||
|
|
||||||
|
} // namespace tca9555
|
||||||
|
} // namespace esphome
|
64
esphome/components/tca9555/tca9555.h
Normal file
64
esphome/components/tca9555/tca9555.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace tca9555 {
|
||||||
|
|
||||||
|
class TCA9555Component : public Component,
|
||||||
|
public i2c::I2CDevice,
|
||||||
|
public gpio_expander::CachedGpioExpander<uint8_t, 16> {
|
||||||
|
public:
|
||||||
|
TCA9555Component() = default;
|
||||||
|
|
||||||
|
/// Check i2c availability and setup masks
|
||||||
|
void setup() override;
|
||||||
|
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool digital_read_hw(uint8_t pin) override;
|
||||||
|
bool digital_read_cache(uint8_t pin) override;
|
||||||
|
void digital_write_hw(uint8_t pin, bool value) override;
|
||||||
|
|
||||||
|
/// Mask for the pin mode - 1 means output, 0 means input
|
||||||
|
uint16_t mode_mask_{0x00};
|
||||||
|
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||||
|
uint16_t output_mask_{0x00};
|
||||||
|
/// The state read in digital_read_hw - 1 means HIGH, 0 means LOW
|
||||||
|
uint16_t input_mask_{0x00};
|
||||||
|
|
||||||
|
bool read_gpio_modes_();
|
||||||
|
bool write_gpio_modes_();
|
||||||
|
bool read_gpio_outputs_();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper class to expose a TCA9555 pin as an internal input GPIO pin.
|
||||||
|
class TCA9555GPIOPin : public GPIOPin, public Parented<TCA9555Component> {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void pin_mode(gpio::Flags flags) override;
|
||||||
|
bool digital_read() override;
|
||||||
|
void digital_write(bool value) override;
|
||||||
|
std::string dump_summary() const override;
|
||||||
|
|
||||||
|
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||||
|
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||||
|
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t pin_;
|
||||||
|
bool inverted_;
|
||||||
|
gpio::Flags flags_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tca9555
|
||||||
|
} // namespace esphome
|
|
@ -1,6 +1,7 @@
|
||||||
#include "tcs34725.h"
|
#include "tcs34725.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace tcs34725 {
|
namespace tcs34725 {
|
||||||
|
@ -211,7 +212,7 @@ void TCS34725Component::update() {
|
||||||
if (raw_c == 0) {
|
if (raw_c == 0) {
|
||||||
channel_c = channel_r = channel_g = channel_b = 0.0f;
|
channel_c = channel_r = channel_g = channel_b = 0.0f;
|
||||||
} else {
|
} else {
|
||||||
float max_count = this->integration_time_ * 1024.0f / 2.4;
|
float max_count = this->integration_time_ <= 153.6f ? this->integration_time_ * 1024.0f / 2.4f : 65535.0f;
|
||||||
float sum = raw_c;
|
float sum = raw_c;
|
||||||
channel_r = raw_r / sum * 100.0f;
|
channel_r = raw_r / sum * 100.0f;
|
||||||
channel_g = raw_g / sum * 100.0f;
|
channel_g = raw_g / sum * 100.0f;
|
||||||
|
@ -254,7 +255,8 @@ void TCS34725Component::update() {
|
||||||
// change integration time an gain to achieve maximum resolution an dynamic range
|
// change integration time an gain to achieve maximum resolution an dynamic range
|
||||||
// calculate optimal integration time to achieve 70% satuaration
|
// calculate optimal integration time to achieve 70% satuaration
|
||||||
float integration_time_ideal;
|
float integration_time_ideal;
|
||||||
integration_time_ideal = 60 / ((float) raw_c / 655.35) * this->integration_time_;
|
|
||||||
|
integration_time_ideal = 60 / ((float) std::max((uint16_t) 1, raw_c) / 655.35f) * this->integration_time_;
|
||||||
|
|
||||||
uint8_t gain_reg_val_new = this->gain_reg_;
|
uint8_t gain_reg_val_new = this->gain_reg_;
|
||||||
// increase gain if less than 20% of white channel used and high integration time
|
// increase gain if less than 20% of white channel used and high integration time
|
||||||
|
|
|
@ -1012,6 +1012,16 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
|
||||||
call.set_mode(mode.c_str());
|
call.set_mode(mode.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request->hasParam("fan_mode")) {
|
||||||
|
auto mode = request->getParam("fan_mode")->value();
|
||||||
|
call.set_fan_mode(mode.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->hasParam("swing_mode")) {
|
||||||
|
auto mode = request->getParam("swing_mode")->value();
|
||||||
|
call.set_swing_mode(mode.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (request->hasParam("target_temperature_high")) {
|
if (request->hasParam("target_temperature_high")) {
|
||||||
auto target_temperature_high = parse_number<float>(request->getParam("target_temperature_high")->value().c_str());
|
auto target_temperature_high = parse_number<float>(request->getParam("target_temperature_high")->value().c_str());
|
||||||
if (target_temperature_high.has_value())
|
if (target_temperature_high.has_value())
|
||||||
|
|
|
@ -130,11 +130,16 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiComponent::wifi_pre_setup_() {
|
void WiFiComponent::wifi_pre_setup_() {
|
||||||
#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
|
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
|
#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
|
||||||
get_mac_address_raw(mac);
|
get_mac_address_raw(mac);
|
||||||
set_mac_address(mac);
|
set_mac_address(mac);
|
||||||
ESP_LOGV(TAG, "Use EFuse MAC without checking CRC: %s", get_mac_address_pretty().c_str());
|
ESP_LOGV(TAG, "Use EFuse MAC without checking CRC: %s", get_mac_address_pretty().c_str());
|
||||||
|
#else
|
||||||
|
if (has_custom_mac_address()) {
|
||||||
|
get_mac_address_raw(mac);
|
||||||
|
set_mac_address(mac);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
esp_err_t err = esp_netif_init();
|
esp_err_t err = esp_netif_init();
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
|
|
|
@ -122,6 +122,7 @@ CONF_CHANNELS = "channels"
|
||||||
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
|
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
|
||||||
CONF_CHECK = "check"
|
CONF_CHECK = "check"
|
||||||
CONF_CHIPSET = "chipset"
|
CONF_CHIPSET = "chipset"
|
||||||
|
CONF_CLEAN_SESSION = "clean_session"
|
||||||
CONF_CLEAR_IMPEDANCE = "clear_impedance"
|
CONF_CLEAR_IMPEDANCE = "clear_impedance"
|
||||||
CONF_CLIENT_CERTIFICATE = "client_certificate"
|
CONF_CLIENT_CERTIFICATE = "client_certificate"
|
||||||
CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key"
|
CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key"
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#define USE_DATETIME_TIME
|
#define USE_DATETIME_TIME
|
||||||
#define USE_DEEP_SLEEP
|
#define USE_DEEP_SLEEP
|
||||||
#define USE_DISPLAY
|
#define USE_DISPLAY
|
||||||
|
#define USE_ESP32_IMPROV_STATE_CALLBACK
|
||||||
#define USE_EVENT
|
#define USE_EVENT
|
||||||
#define USE_FAN
|
#define USE_FAN
|
||||||
#define USE_GRAPH
|
#define USE_GRAPH
|
||||||
|
@ -45,10 +46,10 @@
|
||||||
#define USE_LVGL_BUTTONMATRIX
|
#define USE_LVGL_BUTTONMATRIX
|
||||||
#define USE_LVGL_FONT
|
#define USE_LVGL_FONT
|
||||||
#define USE_LVGL_IMAGE
|
#define USE_LVGL_IMAGE
|
||||||
#define USE_LVGL_KEYBOARD
|
|
||||||
#define USE_LVGL_KEY_LISTENER
|
#define USE_LVGL_KEY_LISTENER
|
||||||
#define USE_LVGL_TOUCHSCREEN
|
#define USE_LVGL_KEYBOARD
|
||||||
#define USE_LVGL_ROTARY_ENCODER
|
#define USE_LVGL_ROTARY_ENCODER
|
||||||
|
#define USE_LVGL_TOUCHSCREEN
|
||||||
#define USE_MDNS
|
#define USE_MDNS
|
||||||
#define USE_MEDIA_PLAYER
|
#define USE_MEDIA_PLAYER
|
||||||
#define USE_MQTT
|
#define USE_MQTT
|
||||||
|
|
|
@ -671,9 +671,17 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame
|
||||||
// match the CRC that goes along with it. For those devices, this
|
// match the CRC that goes along with it. For those devices, this
|
||||||
// work-around reads and uses the MAC address as-is from EFuse,
|
// work-around reads and uses the MAC address as-is from EFuse,
|
||||||
// without doing the CRC check.
|
// without doing the CRC check.
|
||||||
|
if (has_custom_mac_address()) {
|
||||||
|
esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48);
|
||||||
|
} else {
|
||||||
esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48);
|
esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
|
if (has_custom_mac_address()) {
|
||||||
|
esp_efuse_mac_get_custom(mac);
|
||||||
|
} else {
|
||||||
esp_efuse_mac_get_default(mac);
|
esp_efuse_mac_get_default(mac);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#elif defined(USE_ESP8266)
|
#elif defined(USE_ESP8266)
|
||||||
wifi_get_macaddr(STATION_IF, mac);
|
wifi_get_macaddr(STATION_IF, mac);
|
||||||
|
@ -685,20 +693,53 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame
|
||||||
// this should be an error, but that messes with CI checks. #error No mac address method defined
|
// this should be an error, but that messes with CI checks. #error No mac address method defined
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_mac_address() {
|
std::string get_mac_address() {
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
get_mac_address_raw(mac);
|
get_mac_address_raw(mac);
|
||||||
return str_snprintf("%02x%02x%02x%02x%02x%02x", 12, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
return str_snprintf("%02x%02x%02x%02x%02x%02x", 12, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_mac_address_pretty() {
|
std::string get_mac_address_pretty() {
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
get_mac_address_raw(mac);
|
get_mac_address_raw(mac);
|
||||||
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); }
|
void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool has_custom_mac_address() {
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
uint8_t mac[6];
|
||||||
|
#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC)
|
||||||
|
return (esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac);
|
||||||
|
#else
|
||||||
|
return (esp_efuse_mac_get_custom(mac) == ESP_OK) && mac_address_is_valid(mac);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mac_address_is_valid(const uint8_t *mac) {
|
||||||
|
bool is_all_zeros = true;
|
||||||
|
bool is_all_ones = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 6; i++) {
|
||||||
|
if (mac[i] != 0) {
|
||||||
|
is_all_zeros = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uint8_t i = 0; i < 6; i++) {
|
||||||
|
if (mac[i] != 0xFF) {
|
||||||
|
is_all_ones = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !(is_all_zeros || is_all_ones);
|
||||||
|
}
|
||||||
|
|
||||||
void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability
|
void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability
|
||||||
uint32_t start = micros();
|
uint32_t start = micros();
|
||||||
|
|
||||||
|
|
|
@ -635,6 +635,14 @@ std::string get_mac_address_pretty();
|
||||||
void set_mac_address(uint8_t *mac);
|
void set_mac_address(uint8_t *mac);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Check if a custom MAC address is set (ESP32 & variants)
|
||||||
|
/// @return True if a custom MAC address is set (ESP32 & variants), else false
|
||||||
|
bool has_custom_mac_address();
|
||||||
|
|
||||||
|
/// Check if the MAC address is not all zeros or all ones
|
||||||
|
/// @return True if MAC is valid, else false
|
||||||
|
bool mac_address_is_valid(const uint8_t *mac);
|
||||||
|
|
||||||
/// Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
|
/// Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
|
||||||
void delay_microseconds_safe(uint32_t us);
|
void delay_microseconds_safe(uint32_t us);
|
||||||
|
|
||||||
|
|
|
@ -11,16 +11,26 @@ namespace esphome {
|
||||||
|
|
||||||
static const char *const TAG = "ring_buffer";
|
static const char *const TAG = "ring_buffer";
|
||||||
|
|
||||||
|
RingBuffer::~RingBuffer() {
|
||||||
|
if (this->handle_ != nullptr) {
|
||||||
|
vStreamBufferDelete(this->handle_);
|
||||||
|
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
|
allocator.deallocate(this->storage_, this->size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<RingBuffer> RingBuffer::create(size_t len) {
|
std::unique_ptr<RingBuffer> RingBuffer::create(size_t len) {
|
||||||
std::unique_ptr<RingBuffer> rb = make_unique<RingBuffer>();
|
std::unique_ptr<RingBuffer> rb = make_unique<RingBuffer>();
|
||||||
|
|
||||||
|
rb->size_ = len + 1;
|
||||||
|
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
rb->storage_ = allocator.allocate(len + 1);
|
rb->storage_ = allocator.allocate(rb->size_);
|
||||||
if (rb->storage_ == nullptr) {
|
if (rb->storage_ == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
rb->handle_ = xStreamBufferCreateStatic(len + 1, 1, rb->storage_, &rb->structure_);
|
rb->handle_ = xStreamBufferCreateStatic(rb->size_, 1, rb->storage_, &rb->structure_);
|
||||||
ESP_LOGD(TAG, "Created ring buffer with size %u", len);
|
ESP_LOGD(TAG, "Created ring buffer with size %u", len);
|
||||||
return rb;
|
return rb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace esphome {
|
||||||
|
|
||||||
class RingBuffer {
|
class RingBuffer {
|
||||||
public:
|
public:
|
||||||
|
~RingBuffer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reads from the ring buffer, waiting up to a specified number of ticks if necessary.
|
* @brief Reads from the ring buffer, waiting up to a specified number of ticks if necessary.
|
||||||
*
|
*
|
||||||
|
@ -83,6 +85,7 @@ class RingBuffer {
|
||||||
StreamBufferHandle_t handle_;
|
StreamBufferHandle_t handle_;
|
||||||
StaticStreamBuffer_t structure_;
|
StaticStreamBuffer_t structure_;
|
||||||
uint8_t *storage_;
|
uint8_t *storage_;
|
||||||
|
size_t size_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -226,4 +226,6 @@ class _Schema(vol.Schema):
|
||||||
if isinstance(schema, vol.Schema):
|
if isinstance(schema, vol.Schema):
|
||||||
schema = schema.schema
|
schema = schema.schema
|
||||||
ret = super().extend(schema, extra=extra)
|
ret = super().extend(schema, extra=extra)
|
||||||
return _Schema(ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas)
|
return _Schema(
|
||||||
|
ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas.copy()
|
||||||
|
)
|
||||||
|
|
|
@ -8,6 +8,7 @@ uart:
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: bl0906
|
- platform: bl0906
|
||||||
|
id: bl
|
||||||
frequency:
|
frequency:
|
||||||
name: 'Frequency'
|
name: 'Frequency'
|
||||||
temperature:
|
temperature:
|
||||||
|
@ -60,3 +61,9 @@ sensor:
|
||||||
name: 'Total_Energy'
|
name: 'Total_Energy'
|
||||||
total_power:
|
total_power:
|
||||||
name: 'Total_Power'
|
name: 'Total_Power'
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
id: reset
|
||||||
|
on_press:
|
||||||
|
- bl0906.reset_energy: bl
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
ch422g:
|
ch422g:
|
||||||
- id: ch422g_hub
|
- id: ch422g_hub
|
||||||
address: 0x24
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
|
@ -11,10 +10,18 @@ binary_sensor:
|
||||||
number: 1
|
number: 1
|
||||||
mode: INPUT
|
mode: INPUT
|
||||||
inverted: true
|
inverted: true
|
||||||
|
output:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
id: ch422g_output
|
id: ch422_out_0
|
||||||
pin:
|
pin:
|
||||||
ch422g: ch422g_hub
|
ch422g: ch422g_hub
|
||||||
number: 0
|
number: 0
|
||||||
mode: OUTPUT
|
mode: OUTPUT
|
||||||
inverted: false
|
inverted: false
|
||||||
|
- platform: gpio
|
||||||
|
id: ch422_out_11
|
||||||
|
pin:
|
||||||
|
ch422g: ch422g_hub
|
||||||
|
number: 11
|
||||||
|
mode: OUTPUT_OPEN_DRAIN
|
||||||
|
inverted: true
|
||||||
|
|
2
tests/components/ethernet/common-openeth.yaml
Normal file
2
tests/components/ethernet/common-openeth.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ethernet:
|
||||||
|
type: OPENETH
|
1
tests/components/ethernet/test-openeth.esp32-idf.yaml
Normal file
1
tests/components/ethernet/test-openeth.esp32-idf.yaml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<<: !include common-openeth.yaml
|
|
@ -16,7 +16,6 @@ climate:
|
||||||
name: Haier AC
|
name: Haier AC
|
||||||
wifi_signal: true
|
wifi_signal: true
|
||||||
answer_timeout: 200ms
|
answer_timeout: 200ms
|
||||||
beeper: true
|
|
||||||
visual:
|
visual:
|
||||||
min_temperature: 16 °C
|
min_temperature: 16 °C
|
||||||
max_temperature: 30 °C
|
max_temperature: 30 °C
|
||||||
|
@ -38,7 +37,6 @@ climate:
|
||||||
supported_presets:
|
supported_presets:
|
||||||
- AWAY
|
- AWAY
|
||||||
- BOOST
|
- BOOST
|
||||||
- ECO
|
|
||||||
- SLEEP
|
- SLEEP
|
||||||
on_alarm_start:
|
on_alarm_start:
|
||||||
then:
|
then:
|
||||||
|
@ -112,3 +110,15 @@ text_sensor:
|
||||||
name: Haier cleaning status
|
name: Haier cleaning status
|
||||||
protocol_version:
|
protocol_version:
|
||||||
name: Haier protocol version
|
name: Haier protocol version
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
beeper:
|
||||||
|
name: Haier beeper
|
||||||
|
display:
|
||||||
|
name: Haier display
|
||||||
|
health_mode:
|
||||||
|
name: Haier health mode
|
||||||
|
quiet_mode:
|
||||||
|
name: Haier quiet mode
|
||||||
|
|
|
@ -38,8 +38,8 @@ lvgl:
|
||||||
border_width: 0
|
border_width: 0
|
||||||
radius: 0
|
radius: 0
|
||||||
pad_all: 0
|
pad_all: 0
|
||||||
border_color: 0x0077b3
|
border_color: tomato
|
||||||
text_color: 0xFFFFFF
|
text_color: springgreen
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 30
|
height: 30
|
||||||
border_side: [left, top]
|
border_side: [left, top]
|
||||||
|
|
11
tests/components/lvgl/test.host.yaml
Normal file
11
tests/components/lvgl/test.host.yaml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
display:
|
||||||
|
- platform: sdl
|
||||||
|
auto_clear_enabled: false
|
||||||
|
dimensions:
|
||||||
|
width: 480
|
||||||
|
height: 480
|
||||||
|
|
||||||
|
touchscreen:
|
||||||
|
- platform: sdl
|
||||||
|
|
||||||
|
lvgl:
|
|
@ -10,6 +10,7 @@ mqtt:
|
||||||
port: 1883
|
port: 1883
|
||||||
username: debug
|
username: debug
|
||||||
password: debug
|
password: debug
|
||||||
|
clean_session: True
|
||||||
client_id: someclient
|
client_id: someclient
|
||||||
use_abbreviations: false
|
use_abbreviations: false
|
||||||
discovery: true
|
discovery: true
|
||||||
|
|
27
tests/components/tca9555/test.esp32-ard.yaml
Normal file
27
tests/components/tca9555/test.esp32-ard.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_tca9555
|
||||||
|
scl: 16
|
||||||
|
sda: 17
|
||||||
|
|
||||||
|
tca9555:
|
||||||
|
- id: tca9555_hub
|
||||||
|
address: 0x21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_binary_sensor
|
||||||
|
name: TCA9555 Binary Sensor
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_output
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
27
tests/components/tca9555/test.esp32-c3-ard.yaml
Normal file
27
tests/components/tca9555/test.esp32-c3-ard.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_tca9555
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
tca9555:
|
||||||
|
- id: tca9555_hub
|
||||||
|
address: 0x21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_binary_sensor
|
||||||
|
name: TCA9555 Binary Sensor
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_output
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
27
tests/components/tca9555/test.esp32-c3-idf.yaml
Normal file
27
tests/components/tca9555/test.esp32-c3-idf.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_tca9555
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
tca9555:
|
||||||
|
- id: tca9555_hub
|
||||||
|
address: 0x21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_binary_sensor
|
||||||
|
name: TCA9555 Binary Sensor
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_output
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
27
tests/components/tca9555/test.esp32-idf.yaml
Normal file
27
tests/components/tca9555/test.esp32-idf.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_tca9555
|
||||||
|
scl: 16
|
||||||
|
sda: 17
|
||||||
|
|
||||||
|
tca9555:
|
||||||
|
- id: tca9555_hub
|
||||||
|
address: 0x21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_binary_sensor
|
||||||
|
name: TCA9555 Binary Sensor
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_output
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
27
tests/components/tca9555/test.esp8266-ard.yaml
Normal file
27
tests/components/tca9555/test.esp8266-ard.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_tca9555
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
tca9555:
|
||||||
|
- id: tca9555_hub
|
||||||
|
address: 0x21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_binary_sensor
|
||||||
|
name: TCA9555 Binary Sensor
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_output
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
27
tests/components/tca9555/test.rp2040-ard.yaml
Normal file
27
tests/components/tca9555/test.rp2040-ard.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_tca9555
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
tca9555:
|
||||||
|
- id: tca9555_hub
|
||||||
|
address: 0x21
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_binary_sensor
|
||||||
|
name: TCA9555 Binary Sensor
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 1
|
||||||
|
mode: INPUT
|
||||||
|
inverted: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: tca9555_output
|
||||||
|
pin:
|
||||||
|
tca9555: tca9555_hub
|
||||||
|
number: 0
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: false
|
Loading…
Reference in a new issue