mirror of
https://github.com/esphome/esphome.git
synced 2026-09-01 18:46:02 +00:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -11,6 +11,7 @@ from esphome.const import (
|
||||
CONF_ICON,
|
||||
CONF_ID,
|
||||
CONF_MQTT_ID,
|
||||
CONF_MQTT_JSON_STATE_PAYLOAD,
|
||||
CONF_ON_IDLE,
|
||||
CONF_ON_OPEN,
|
||||
CONF_POSITION,
|
||||
@@ -119,6 +120,9 @@ _COVER_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
|
||||
cv.Optional(CONF_MQTT_JSON_STATE_PAYLOAD): cv.All(
|
||||
cv.requires_component("mqtt"), cv.boolean
|
||||
),
|
||||
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
||||
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||
@@ -148,6 +152,22 @@ _COVER_SCHEMA = (
|
||||
_COVER_SCHEMA.add_extra(entity_duplicate_validator("cover"))
|
||||
|
||||
|
||||
def _validate_mqtt_state_topics(config):
|
||||
if config.get(CONF_MQTT_JSON_STATE_PAYLOAD):
|
||||
if CONF_POSITION_STATE_TOPIC in config:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_POSITION_STATE_TOPIC}' cannot be used with '{CONF_MQTT_JSON_STATE_PAYLOAD}: true'"
|
||||
)
|
||||
if CONF_TILT_STATE_TOPIC in config:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_TILT_STATE_TOPIC}' cannot be used with '{CONF_MQTT_JSON_STATE_PAYLOAD}: true'"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
_COVER_SCHEMA.add_extra(_validate_mqtt_state_topics)
|
||||
|
||||
|
||||
def cover_schema(
|
||||
class_: MockObjClass,
|
||||
*,
|
||||
@@ -195,6 +215,9 @@ async def setup_cover_core_(var, config):
|
||||
position_command_topic := config.get(CONF_POSITION_COMMAND_TOPIC)
|
||||
) is not None:
|
||||
cg.add(mqtt_.set_custom_position_command_topic(position_command_topic))
|
||||
if config.get(CONF_MQTT_JSON_STATE_PAYLOAD):
|
||||
cg.add_define("USE_MQTT_COVER_JSON")
|
||||
cg.add(mqtt_.set_use_json_format(True))
|
||||
if (tilt_state_topic := config.get(CONF_TILT_STATE_TOPIC)) is not None:
|
||||
cg.add(mqtt_.set_custom_tilt_state_topic(tilt_state_topic))
|
||||
if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None:
|
||||
|
||||
@@ -67,17 +67,26 @@ void MQTTCoverComponent::dump_config() {
|
||||
auto traits = this->cover_->get_traits();
|
||||
bool has_command_topic = traits.get_supports_position() || !traits.get_supports_tilt();
|
||||
LOG_MQTT_COMPONENT(true, has_command_topic);
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
#ifdef USE_MQTT_COVER_JSON
|
||||
if (this->use_json_format_) {
|
||||
ESP_LOGCONFIG(TAG, " JSON State Payload: YES");
|
||||
} else {
|
||||
#endif
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGCONFIG(TAG, " Position State Topic: '%s'", this->get_position_state_topic_to(topic_buf).c_str());
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
ESP_LOGCONFIG(TAG, " Tilt State Topic: '%s'", this->get_tilt_state_topic_to(topic_buf).c_str());
|
||||
}
|
||||
#ifdef USE_MQTT_COVER_JSON
|
||||
}
|
||||
#endif
|
||||
if (traits.get_supports_position()) {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Position State Topic: '%s'\n"
|
||||
" Position Command Topic: '%s'",
|
||||
this->get_position_state_topic().c_str(), this->get_position_command_topic().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Position Command Topic: '%s'", this->get_position_command_topic_to(topic_buf).c_str());
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Tilt State Topic: '%s'\n"
|
||||
" Tilt Command Topic: '%s'",
|
||||
this->get_tilt_state_topic().c_str(), this->get_tilt_command_topic().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Tilt Command Topic: '%s'", this->get_tilt_command_topic_to(topic_buf).c_str());
|
||||
}
|
||||
}
|
||||
void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
@@ -92,13 +101,33 @@ void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConf
|
||||
if (traits.get_is_assumed_state()) {
|
||||
root[MQTT_OPTIMISTIC] = true;
|
||||
}
|
||||
if (traits.get_supports_position()) {
|
||||
root[MQTT_POSITION_TOPIC] = this->get_position_state_topic();
|
||||
root[MQTT_SET_POSITION_TOPIC] = this->get_position_command_topic();
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
root[MQTT_TILT_STATUS_TOPIC] = this->get_tilt_state_topic();
|
||||
root[MQTT_TILT_COMMAND_TOPIC] = this->get_tilt_command_topic();
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
#ifdef USE_MQTT_COVER_JSON
|
||||
if (this->use_json_format_) {
|
||||
// JSON mode: all state published to state_topic as JSON, use templates to extract
|
||||
root[MQTT_VALUE_TEMPLATE] = ESPHOME_F("{{ value_json.state }}");
|
||||
if (traits.get_supports_position()) {
|
||||
root[MQTT_POSITION_TOPIC] = this->get_state_topic_to_(topic_buf);
|
||||
root[MQTT_POSITION_TEMPLATE] = ESPHOME_F("{{ value_json.position }}");
|
||||
root[MQTT_SET_POSITION_TOPIC] = this->get_position_command_topic_to(topic_buf);
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
root[MQTT_TILT_STATUS_TOPIC] = this->get_state_topic_to_(topic_buf);
|
||||
root[MQTT_TILT_STATUS_TEMPLATE] = ESPHOME_F("{{ value_json.tilt }}");
|
||||
root[MQTT_TILT_COMMAND_TOPIC] = this->get_tilt_command_topic_to(topic_buf);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Standard mode: separate topics for position and tilt
|
||||
if (traits.get_supports_position()) {
|
||||
root[MQTT_POSITION_TOPIC] = this->get_position_state_topic_to(topic_buf);
|
||||
root[MQTT_SET_POSITION_TOPIC] = this->get_position_command_topic_to(topic_buf);
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
root[MQTT_TILT_STATUS_TOPIC] = this->get_tilt_state_topic_to(topic_buf);
|
||||
root[MQTT_TILT_COMMAND_TOPIC] = this->get_tilt_command_topic_to(topic_buf);
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_tilt() && !traits.get_supports_position()) {
|
||||
config.command_topic = false;
|
||||
@@ -111,8 +140,24 @@ const EntityBase *MQTTCoverComponent::get_entity() const { return this->cover_;
|
||||
bool MQTTCoverComponent::send_initial_state() { return this->publish_state(); }
|
||||
bool MQTTCoverComponent::publish_state() {
|
||||
auto traits = this->cover_->get_traits();
|
||||
bool success = true;
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
#ifdef USE_MQTT_COVER_JSON
|
||||
if (this->use_json_format_) {
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this, traits](JsonObject root) {
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("state")] = cover_state_to_mqtt_str(this->cover_->current_operation, this->cover_->position,
|
||||
traits.get_supports_position());
|
||||
if (traits.get_supports_position()) {
|
||||
root[ESPHOME_F("position")] = static_cast<int>(roundf(this->cover_->position * 100));
|
||||
}
|
||||
if (traits.get_supports_tilt()) {
|
||||
root[ESPHOME_F("tilt")] = static_cast<int>(roundf(this->cover_->tilt * 100));
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
});
|
||||
}
|
||||
#endif
|
||||
bool success = true;
|
||||
if (traits.get_supports_position()) {
|
||||
char pos[VALUE_ACCURACY_MAX_LEN];
|
||||
size_t len = value_accuracy_to_buf(pos, roundf(this->cover_->position * 100), 0);
|
||||
|
||||
@@ -27,12 +27,18 @@ class MQTTCoverComponent : public mqtt::MQTTComponent {
|
||||
bool publish_state();
|
||||
|
||||
void dump_config() override;
|
||||
#ifdef USE_MQTT_COVER_JSON
|
||||
void set_use_json_format(bool use_json_format) { this->use_json_format_ = use_json_format; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
const char *component_type() const override;
|
||||
const EntityBase *get_entity() const override;
|
||||
|
||||
cover::Cover *cover_;
|
||||
#ifdef USE_MQTT_COVER_JSON
|
||||
bool use_json_format_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -639,6 +639,7 @@ CONF_MOVEMENT_COUNTER = "movement_counter"
|
||||
CONF_MOVING_DISTANCE = "moving_distance"
|
||||
CONF_MQTT = "mqtt"
|
||||
CONF_MQTT_ID = "mqtt_id"
|
||||
CONF_MQTT_JSON_STATE_PAYLOAD = "mqtt_json_state_payload"
|
||||
CONF_MULTIPLE = "multiple"
|
||||
CONF_MULTIPLEXER = "multiplexer"
|
||||
CONF_MULTIPLY = "multiply"
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
#define USE_MD5
|
||||
#define USE_SHA256
|
||||
#define USE_MQTT
|
||||
#define USE_MQTT_COVER_JSON
|
||||
#define USE_NETWORK
|
||||
#define USE_ONLINE_IMAGE_BMP_SUPPORT
|
||||
#define USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||
|
||||
@@ -219,6 +219,7 @@ cover:
|
||||
name: Template Cover
|
||||
state_topic: some/topic/cover
|
||||
qos: 2
|
||||
mqtt_json_state_payload: true
|
||||
lambda: |-
|
||||
if (id(some_binary_sensor).state) {
|
||||
return COVER_OPEN;
|
||||
@@ -231,6 +232,53 @@ cover:
|
||||
stop_action:
|
||||
- logger.log: stop_action
|
||||
optimistic: true
|
||||
- platform: template
|
||||
name: Template Cover with Position and Tilt
|
||||
state_topic: some/topic/cover_pt
|
||||
position_state_topic: some/topic/cover_pt/position
|
||||
position_command_topic: some/topic/cover_pt/position/set
|
||||
tilt_state_topic: some/topic/cover_pt/tilt
|
||||
tilt_command_topic: some/topic/cover_pt/tilt/set
|
||||
qos: 2
|
||||
has_position: true
|
||||
lambda: |-
|
||||
if (id(some_binary_sensor).state) {
|
||||
return COVER_OPEN;
|
||||
}
|
||||
return COVER_CLOSED;
|
||||
position_action:
|
||||
- logger.log: position_action
|
||||
tilt_action:
|
||||
- logger.log: tilt_action
|
||||
open_action:
|
||||
- logger.log: open_action
|
||||
close_action:
|
||||
- logger.log: close_action
|
||||
stop_action:
|
||||
- logger.log: stop_action
|
||||
optimistic: true
|
||||
- platform: template
|
||||
name: Template Cover with Position and Tilt JSON
|
||||
state_topic: some/topic/cover_pt_json
|
||||
qos: 2
|
||||
mqtt_json_state_payload: true
|
||||
has_position: true
|
||||
lambda: |-
|
||||
if (id(some_binary_sensor).state) {
|
||||
return COVER_OPEN;
|
||||
}
|
||||
return COVER_CLOSED;
|
||||
position_action:
|
||||
- logger.log: position_action
|
||||
tilt_action:
|
||||
- logger.log: tilt_action
|
||||
open_action:
|
||||
- logger.log: open_action
|
||||
close_action:
|
||||
- logger.log: close_action
|
||||
stop_action:
|
||||
- logger.log: stop_action
|
||||
optimistic: true
|
||||
|
||||
datetime:
|
||||
- platform: template
|
||||
|
||||
Reference in New Issue
Block a user