diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 1c7d9f86dd..571f5c8317 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -2,9 +2,9 @@ #ifdef USE_MQTT -#include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/core/version.h" #include "mqtt_const.h" @@ -28,15 +28,15 @@ std::string MQTTComponent::get_default_topic_for_(const std::string &suffix) con } std::string MQTTComponent::get_state_topic_() const { - if (this->custom_state_topic_.empty()) - return this->get_default_topic_for_("state"); - return this->custom_state_topic_; + if (this->has_custom_state_topic_) + return this->custom_state_topic_; + return this->get_default_topic_for_("state"); } std::string MQTTComponent::get_command_topic_() const { - if (this->custom_command_topic_.empty()) - return this->get_default_topic_for_("command"); - return this->custom_command_topic_; + if (this->has_custom_command_topic_) + return this->custom_command_topic_; + return this->get_default_topic_for_("command"); } bool MQTTComponent::publish(const std::string &topic, const std::string &payload) { @@ -171,9 +171,11 @@ float MQTTComponent::get_setup_priority() const { return setup_priority::AFTER_C void MQTTComponent::disable_discovery() { this->discovery_enabled_ = false; } void MQTTComponent::set_custom_state_topic(const std::string &custom_state_topic) { this->custom_state_topic_ = custom_state_topic; + this->has_custom_state_topic_ = true; } void MQTTComponent::set_custom_command_topic(const std::string &custom_command_topic) { this->custom_command_topic_ = custom_command_topic; + this->has_custom_command_topic_ = true; } void MQTTComponent::set_command_retain(bool command_retain) { this->command_retain_ = command_retain; } @@ -240,7 +242,20 @@ bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connec std::string MQTTComponent::friendly_name() const { return this->get_entity()->get_name(); } std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); } bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); } -bool MQTTComponent::is_internal() { return this->get_entity()->is_internal(); } +bool MQTTComponent::is_internal() { + if ((this->get_state_topic_().empty()) || (this->get_command_topic_().empty())) { + // If both state_topic and command_topic are empty, then the entity is internal to mqtt + return true; + } + + if (this->has_custom_state_topic_ || this->has_custom_command_topic_) { + // If a custom state_topic or command_topic is set, then the entity is not internal to mqtt + return false; + } + + // Use ESPHome's entity internal state + return this->get_entity()->is_internal(); +} } // namespace mqtt } // namespace esphome diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 16a00cfdde..aacfe8891f 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -190,6 +190,9 @@ class MQTTComponent : public Component { std::string custom_state_topic_{}; std::string custom_command_topic_{}; + bool has_custom_state_topic_{false}; + bool has_custom_command_topic_{false}; + bool command_retain_{false}; bool retain_{true}; bool discovery_enabled_{true}; diff --git a/esphome/components/mqtt/mqtt_text.cpp b/esphome/components/mqtt/mqtt_text.cpp index 58978faead..cb852b64cd 100644 --- a/esphome/components/mqtt/mqtt_text.cpp +++ b/esphome/components/mqtt/mqtt_text.cpp @@ -27,7 +27,7 @@ void MQTTTextComponent::setup() { void MQTTTextComponent::dump_config() { ESP_LOGCONFIG(TAG, "MQTT text '%s':", this->text_->get_name().c_str()); - LOG_MQTT_COMPONENT(true, false) + LOG_MQTT_COMPONENT(true, true) } std::string MQTTTextComponent::component_type() const { return "text"; } diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index d0a84b99ff..ee90e0e410 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -68,9 +68,18 @@ void SX1509Component::digital_write(uint8_t pin, bool bit_value) { uint16_t temp_reg_data = 0; this->read_byte_16(REG_DATA_B, &temp_reg_data); if (bit_value) { - temp_reg_data |= (1 << pin); + output_state_ |= (1 << pin); // set bit in shadow register } else { - temp_reg_data &= ~(1 << pin); + output_state_ &= ~(1 << pin); // reset bit shadow register + } + for (uint16_t b = 0x8000; b; b >>= 1) { + if ((~ddr_mask_) & b) { // transfer bits of outputs, but don't mess with inputs + if (output_state_ & b) { + temp_reg_data |= b; + } else { + temp_reg_data &= ~b; + } + } } this->write_byte_16(REG_DATA_B, temp_reg_data); } @@ -134,6 +143,7 @@ void SX1509Component::setup_led_driver(uint8_t pin) { this->read_byte_16(REG_DATA_B, &temp_word); temp_word &= ~(1 << pin); + output_state_ &= ~(1 << pin); this->write_byte_16(REG_DATA_B, temp_word); } diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 8e3b41e233..9e4f31aab0 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -61,6 +61,7 @@ class SX1509Component : public Component, public i2c::I2CDevice { uint16_t ddr_mask_ = 0x00; uint16_t input_mask_ = 0x00; uint16_t port_mask_ = 0x00; + uint16_t output_state_ = 0x00; bool has_keypad_ = false; uint8_t rows_ = 0; uint8_t cols_ = 0; diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 1b95370fdf..d2b381215e 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1047,6 +1047,8 @@ def ipv4(value): def _valid_topic(value): """Validate that this is a valid topic name/filter.""" + if value is None: # Used to disable publishing and subscribing + return "" if isinstance(value, dict): raise Invalid("Can't use dictionary with topic") value = string(value) diff --git a/tests/test5.yaml b/tests/test5.yaml index d87a7f50d4..82c201f017 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -64,6 +64,7 @@ mqtt: discovery: true discovery_prefix: homeassistant idf_send_async: false + log_topic: on_message: topic: testing/sensor/testing_sensor/state qos: 0 @@ -403,6 +404,7 @@ sensor: update_interval: 1s - platform: internal_temperature name: Internal Temperature + state_topic: - platform: selec_meter total_active_energy: name: SelecEM2M Total Active Energy