Compare commits

...

17 Commits

Author SHA1 Message Date
J. Nick Koston
b445d46888 Merge remote-tracking branch 'upstream/dev' into mqtt_enum_flash
# Conflicts:
#	esphome/components/mqtt/mqtt_alarm_control_panel.cpp
#	esphome/components/mqtt/mqtt_component.cpp
#	esphome/components/mqtt/mqtt_component.h
#	esphome/components/mqtt/mqtt_cover.cpp
#	esphome/components/mqtt/mqtt_valve.cpp
2026-01-26 17:30:37 -10:00
J. Nick Koston
6da4f95258 [mqtt] Refactor state publishing with dedicated enum-to-string helpers 2026-01-25 23:04:33 -10:00
J. Nick Koston
0207e6e8b5 [mqtt] Refactor state publishing with dedicated enum-to-string helpers 2026-01-25 23:04:26 -10:00
J. Nick Koston
ee6e12913c [mqtt] Refactor state publishing with dedicated enum-to-string helpers 2026-01-25 23:02:18 -10:00
J. Nick Koston
d95ef154aa Merge branch 'dev' into mqtt_stack_part_2 2026-01-25 22:37:19 -10:00
J. Nick Koston
e006216ad3 Merge branch 'dev' into mqtt_stack_part_2 2026-01-21 18:37:55 -10:00
J. Nick Koston
d66d05dbfc [mqtt] Use stack buffers for publish_state() topic building 2026-01-21 11:08:06 -10:00
J. Nick Koston
bba447e656 Merge branch 'dev' into mqtt_formatting 2026-01-21 11:06:26 -10:00
J. Nick Koston
77b6720a25 Merge branch 'dev' into mqtt_formatting 2026-01-19 17:40:37 -10:00
J. Nick Koston
2f8f052f43 Merge branch 'dev' into mqtt_formatting 2026-01-18 18:44:11 -10:00
J. Nick Koston
86e70c7e76 more 2026-01-17 07:38:51 -10:00
J. Nick Koston
40025bb277 tweaks to reduce RAM 2026-01-17 07:34:22 -10:00
J. Nick Koston
438bb96687 tweaks to reduce RAM 2026-01-17 07:28:44 -10:00
J. Nick Koston
c1e1325af2 Merge branch 'dev' into mqtt_formatting 2026-01-17 07:24:11 -10:00
pre-commit-ci-lite[bot]
944194e04e [pre-commit.ci lite] apply automatic fixes 2026-01-15 00:02:35 +00:00
J. Nick Koston
d27d6d64da Update esphome/components/mqtt/mqtt_component.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-14 14:00:57 -10:00
J. Nick Koston
2182d1e9f0 [mqtt] Use stack buffers for discovery message formatting 2026-01-14 13:57:45 -10:00
7 changed files with 254 additions and 197 deletions

View File

@@ -1,5 +1,6 @@
#include "mqtt_alarm_control_panel.h" #include "mqtt_alarm_control_panel.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "mqtt_const.h" #include "mqtt_const.h"
@@ -12,6 +13,33 @@ static const char *const TAG = "mqtt.alarm_control_panel";
using namespace esphome::alarm_control_panel; using namespace esphome::alarm_control_panel;
static ProgmemStr alarm_state_to_mqtt_str(AlarmControlPanelState state) {
switch (state) {
case ACP_STATE_DISARMED:
return ESPHOME_F("disarmed");
case ACP_STATE_ARMED_HOME:
return ESPHOME_F("armed_home");
case ACP_STATE_ARMED_AWAY:
return ESPHOME_F("armed_away");
case ACP_STATE_ARMED_NIGHT:
return ESPHOME_F("armed_night");
case ACP_STATE_ARMED_VACATION:
return ESPHOME_F("armed_vacation");
case ACP_STATE_ARMED_CUSTOM_BYPASS:
return ESPHOME_F("armed_custom_bypass");
case ACP_STATE_PENDING:
return ESPHOME_F("pending");
case ACP_STATE_ARMING:
return ESPHOME_F("arming");
case ACP_STATE_DISARMING:
return ESPHOME_F("disarming");
case ACP_STATE_TRIGGERED:
return ESPHOME_F("triggered");
default:
return ESPHOME_F("unknown");
}
}
MQTTAlarmControlPanelComponent::MQTTAlarmControlPanelComponent(AlarmControlPanel *alarm_control_panel) MQTTAlarmControlPanelComponent::MQTTAlarmControlPanelComponent(AlarmControlPanel *alarm_control_panel)
: alarm_control_panel_(alarm_control_panel) {} : alarm_control_panel_(alarm_control_panel) {}
void MQTTAlarmControlPanelComponent::setup() { void MQTTAlarmControlPanelComponent::setup() {
@@ -84,43 +112,9 @@ const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return th
bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); } bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); }
bool MQTTAlarmControlPanelComponent::publish_state() { bool MQTTAlarmControlPanelComponent::publish_state() {
const char *state_s;
switch (this->alarm_control_panel_->get_state()) {
case ACP_STATE_DISARMED:
state_s = "disarmed";
break;
case ACP_STATE_ARMED_HOME:
state_s = "armed_home";
break;
case ACP_STATE_ARMED_AWAY:
state_s = "armed_away";
break;
case ACP_STATE_ARMED_NIGHT:
state_s = "armed_night";
break;
case ACP_STATE_ARMED_VACATION:
state_s = "armed_vacation";
break;
case ACP_STATE_ARMED_CUSTOM_BYPASS:
state_s = "armed_custom_bypass";
break;
case ACP_STATE_PENDING:
state_s = "pending";
break;
case ACP_STATE_ARMING:
state_s = "arming";
break;
case ACP_STATE_DISARMING:
state_s = "disarming";
break;
case ACP_STATE_TRIGGERED:
state_s = "triggered";
break;
default:
state_s = "unknown";
}
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
return this->publish(this->get_state_topic_to_(topic_buf), state_s); return this->publish(this->get_state_topic_to_(topic_buf),
alarm_state_to_mqtt_str(this->alarm_control_panel_->get_state()));
} }
} // namespace esphome::mqtt } // namespace esphome::mqtt

View File

@@ -1,5 +1,6 @@
#include "mqtt_climate.h" #include "mqtt_climate.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "mqtt_const.h" #include "mqtt_const.h"
@@ -12,6 +13,111 @@ static const char *const TAG = "mqtt.climate";
using namespace esphome::climate; using namespace esphome::climate;
static ProgmemStr climate_mode_to_mqtt_str(ClimateMode mode) {
switch (mode) {
case CLIMATE_MODE_OFF:
return ESPHOME_F("off");
case CLIMATE_MODE_HEAT_COOL:
return ESPHOME_F("heat_cool");
case CLIMATE_MODE_AUTO:
return ESPHOME_F("auto");
case CLIMATE_MODE_COOL:
return ESPHOME_F("cool");
case CLIMATE_MODE_HEAT:
return ESPHOME_F("heat");
case CLIMATE_MODE_FAN_ONLY:
return ESPHOME_F("fan_only");
case CLIMATE_MODE_DRY:
return ESPHOME_F("dry");
default:
return ESPHOME_F("unknown");
}
}
static ProgmemStr climate_action_to_mqtt_str(ClimateAction action) {
switch (action) {
case CLIMATE_ACTION_OFF:
return ESPHOME_F("off");
case CLIMATE_ACTION_COOLING:
return ESPHOME_F("cooling");
case CLIMATE_ACTION_HEATING:
return ESPHOME_F("heating");
case CLIMATE_ACTION_IDLE:
return ESPHOME_F("idle");
case CLIMATE_ACTION_DRYING:
return ESPHOME_F("drying");
case CLIMATE_ACTION_FAN:
return ESPHOME_F("fan");
default:
return ESPHOME_F("unknown");
}
}
static ProgmemStr climate_fan_mode_to_mqtt_str(ClimateFanMode fan_mode) {
switch (fan_mode) {
case CLIMATE_FAN_ON:
return ESPHOME_F("on");
case CLIMATE_FAN_OFF:
return ESPHOME_F("off");
case CLIMATE_FAN_AUTO:
return ESPHOME_F("auto");
case CLIMATE_FAN_LOW:
return ESPHOME_F("low");
case CLIMATE_FAN_MEDIUM:
return ESPHOME_F("medium");
case CLIMATE_FAN_HIGH:
return ESPHOME_F("high");
case CLIMATE_FAN_MIDDLE:
return ESPHOME_F("middle");
case CLIMATE_FAN_FOCUS:
return ESPHOME_F("focus");
case CLIMATE_FAN_DIFFUSE:
return ESPHOME_F("diffuse");
case CLIMATE_FAN_QUIET:
return ESPHOME_F("quiet");
default:
return ESPHOME_F("unknown");
}
}
static ProgmemStr climate_swing_mode_to_mqtt_str(ClimateSwingMode swing_mode) {
switch (swing_mode) {
case CLIMATE_SWING_OFF:
return ESPHOME_F("off");
case CLIMATE_SWING_BOTH:
return ESPHOME_F("both");
case CLIMATE_SWING_VERTICAL:
return ESPHOME_F("vertical");
case CLIMATE_SWING_HORIZONTAL:
return ESPHOME_F("horizontal");
default:
return ESPHOME_F("unknown");
}
}
static ProgmemStr climate_preset_to_mqtt_str(ClimatePreset preset) {
switch (preset) {
case CLIMATE_PRESET_NONE:
return ESPHOME_F("none");
case CLIMATE_PRESET_HOME:
return ESPHOME_F("home");
case CLIMATE_PRESET_ECO:
return ESPHOME_F("eco");
case CLIMATE_PRESET_AWAY:
return ESPHOME_F("away");
case CLIMATE_PRESET_BOOST:
return ESPHOME_F("boost");
case CLIMATE_PRESET_COMFORT:
return ESPHOME_F("comfort");
case CLIMATE_PRESET_SLEEP:
return ESPHOME_F("sleep");
case CLIMATE_PRESET_ACTIVITY:
return ESPHOME_F("activity");
default:
return ESPHOME_F("unknown");
}
}
void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto traits = this->device_->get_traits(); auto traits = this->device_->get_traits();
@@ -260,34 +366,8 @@ const EntityBase *MQTTClimateComponent::get_entity() const { return this->device
bool MQTTClimateComponent::publish_state_() { bool MQTTClimateComponent::publish_state_() {
auto traits = this->device_->get_traits(); auto traits = this->device_->get_traits();
// mode // mode
const char *mode_s;
switch (this->device_->mode) {
case CLIMATE_MODE_OFF:
mode_s = "off";
break;
case CLIMATE_MODE_AUTO:
mode_s = "auto";
break;
case CLIMATE_MODE_COOL:
mode_s = "cool";
break;
case CLIMATE_MODE_HEAT:
mode_s = "heat";
break;
case CLIMATE_MODE_FAN_ONLY:
mode_s = "fan_only";
break;
case CLIMATE_MODE_DRY:
mode_s = "dry";
break;
case CLIMATE_MODE_HEAT_COOL:
mode_s = "heat_cool";
break;
default:
mode_s = "unknown";
}
bool success = true; bool success = true;
if (!this->publish(this->get_mode_state_topic(), mode_s)) if (!this->publish(this->get_mode_state_topic(), climate_mode_to_mqtt_str(this->device_->mode)))
success = false; success = false;
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals(); int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
@@ -327,134 +407,37 @@ bool MQTTClimateComponent::publish_state_() {
} }
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) { if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
std::string payload; if (this->device_->has_custom_preset()) {
if (this->device_->preset.has_value()) { if (!this->publish(this->get_preset_state_topic(), this->device_->get_custom_preset()))
switch (this->device_->preset.value()) { success = false;
case CLIMATE_PRESET_NONE: } else if (this->device_->preset.has_value()) {
payload = "none"; if (!this->publish(this->get_preset_state_topic(), climate_preset_to_mqtt_str(this->device_->preset.value())))
break; success = false;
case CLIMATE_PRESET_HOME: } else if (!this->publish(this->get_preset_state_topic(), "")) {
payload = "home";
break;
case CLIMATE_PRESET_AWAY:
payload = "away";
break;
case CLIMATE_PRESET_BOOST:
payload = "boost";
break;
case CLIMATE_PRESET_COMFORT:
payload = "comfort";
break;
case CLIMATE_PRESET_ECO:
payload = "eco";
break;
case CLIMATE_PRESET_SLEEP:
payload = "sleep";
break;
case CLIMATE_PRESET_ACTIVITY:
payload = "activity";
break;
default:
payload = "unknown";
}
}
if (this->device_->has_custom_preset())
payload = this->device_->get_custom_preset().c_str();
if (!this->publish(this->get_preset_state_topic(), payload))
success = false; success = false;
}
} }
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) {
const char *payload; if (!this->publish(this->get_action_state_topic(), climate_action_to_mqtt_str(this->device_->action)))
switch (this->device_->action) {
case CLIMATE_ACTION_OFF:
payload = "off";
break;
case CLIMATE_ACTION_COOLING:
payload = "cooling";
break;
case CLIMATE_ACTION_HEATING:
payload = "heating";
break;
case CLIMATE_ACTION_IDLE:
payload = "idle";
break;
case CLIMATE_ACTION_DRYING:
payload = "drying";
break;
case CLIMATE_ACTION_FAN:
payload = "fan";
break;
default:
payload = "unknown";
}
if (!this->publish(this->get_action_state_topic(), payload))
success = false; success = false;
} }
if (traits.get_supports_fan_modes()) { if (traits.get_supports_fan_modes()) {
std::string payload; if (this->device_->has_custom_fan_mode()) {
if (this->device_->fan_mode.has_value()) { if (!this->publish(this->get_fan_mode_state_topic(), this->device_->get_custom_fan_mode()))
switch (this->device_->fan_mode.value()) { success = false;
case CLIMATE_FAN_ON: } else if (this->device_->fan_mode.has_value()) {
payload = "on"; if (!this->publish(this->get_fan_mode_state_topic(),
break; climate_fan_mode_to_mqtt_str(this->device_->fan_mode.value())))
case CLIMATE_FAN_OFF: success = false;
payload = "off"; } else if (!this->publish(this->get_fan_mode_state_topic(), "")) {
break;
case CLIMATE_FAN_AUTO:
payload = "auto";
break;
case CLIMATE_FAN_LOW:
payload = "low";
break;
case CLIMATE_FAN_MEDIUM:
payload = "medium";
break;
case CLIMATE_FAN_HIGH:
payload = "high";
break;
case CLIMATE_FAN_MIDDLE:
payload = "middle";
break;
case CLIMATE_FAN_FOCUS:
payload = "focus";
break;
case CLIMATE_FAN_DIFFUSE:
payload = "diffuse";
break;
case CLIMATE_FAN_QUIET:
payload = "quiet";
break;
default:
payload = "unknown";
}
}
if (this->device_->has_custom_fan_mode())
payload = this->device_->get_custom_fan_mode().c_str();
if (!this->publish(this->get_fan_mode_state_topic(), payload))
success = false; success = false;
}
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
const char *payload; if (!this->publish(this->get_swing_mode_state_topic(), climate_swing_mode_to_mqtt_str(this->device_->swing_mode)))
switch (this->device_->swing_mode) {
case CLIMATE_SWING_OFF:
payload = "off";
break;
case CLIMATE_SWING_BOTH:
payload = "both";
break;
case CLIMATE_SWING_VERTICAL:
payload = "vertical";
break;
case CLIMATE_SWING_HORIZONTAL:
payload = "horizontal";
break;
default:
payload = "unknown";
}
if (!this->publish(this->get_swing_mode_state_topic(), payload))
success = false; success = false;
} }

View File

@@ -5,6 +5,7 @@
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/version.h" #include "esphome/core/version.h"
#include "mqtt_const.h" #include "mqtt_const.h"
@@ -149,6 +150,22 @@ bool MQTTComponent::publish(const char *topic, const char *payload) {
return this->publish(topic, payload, strlen(payload)); return this->publish(topic, payload, strlen(payload));
} }
#ifdef USE_ESP8266
bool MQTTComponent::publish(const std::string &topic, ProgmemStr payload) {
return this->publish(topic.c_str(), payload);
}
bool MQTTComponent::publish(const char *topic, ProgmemStr payload) {
if (topic[0] == '\0')
return false;
// On ESP8266, ProgmemStr is __FlashStringHelper* - need to copy from flash
char buf[64];
strncpy_P(buf, reinterpret_cast<const char *>(payload), sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
return global_mqtt_client->publish(topic, buf, strlen(buf), this->qos_, this->retain_);
}
#endif
bool MQTTComponent::publish_json(const std::string &topic, const json::json_build_t &f) { bool MQTTComponent::publish_json(const std::string &topic, const json::json_build_t &f) {
return this->publish_json(topic.c_str(), f); return this->publish_json(topic.c_str(), f);
} }

View File

@@ -9,6 +9,7 @@
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/entity_base.h" #include "esphome/core/entity_base.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h" #include "esphome/core/string_ref.h"
#include "mqtt_client.h" #include "mqtt_client.h"
@@ -157,6 +158,15 @@ class MQTTComponent : public Component {
*/ */
bool publish(const std::string &topic, const char *payload, size_t payload_length); bool publish(const std::string &topic, const char *payload, size_t payload_length);
/** Send a MQTT message.
*
* @param topic The topic.
* @param payload The null-terminated payload.
*/
bool publish(const std::string &topic, const char *payload) {
return this->publish(topic.c_str(), payload, strlen(payload));
}
/** Send a MQTT message (no heap allocation for topic). /** Send a MQTT message (no heap allocation for topic).
* *
* @param topic The topic as C string. * @param topic The topic as C string.
@@ -189,6 +199,29 @@ class MQTTComponent : public Component {
*/ */
bool publish(StringRef topic, const char *payload) { return this->publish(topic.c_str(), payload); } bool publish(StringRef topic, const char *payload) { return this->publish(topic.c_str(), payload); }
#ifdef USE_ESP8266
/** Send a MQTT message with a PROGMEM string payload.
*
* @param topic The topic.
* @param payload The payload (ProgmemStr - stored in flash on ESP8266).
*/
bool publish(const std::string &topic, ProgmemStr payload);
/** Send a MQTT message with a PROGMEM string payload (no heap allocation for topic).
*
* @param topic The topic as C string.
* @param payload The payload (ProgmemStr - stored in flash on ESP8266).
*/
bool publish(const char *topic, ProgmemStr payload);
/** Send a MQTT message with a PROGMEM string payload (no heap allocation for topic).
*
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
* @param payload The payload (ProgmemStr - stored in flash on ESP8266).
*/
bool publish(StringRef topic, ProgmemStr payload) { return this->publish(topic.c_str(), payload); }
#endif
/** Construct and send a JSON MQTT message. /** Construct and send a JSON MQTT message.
* *
* @param topic The topic. * @param topic The topic.

View File

@@ -1,5 +1,6 @@
#include "mqtt_cover.h" #include "mqtt_cover.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "mqtt_const.h" #include "mqtt_const.h"
@@ -12,6 +13,20 @@ static const char *const TAG = "mqtt.cover";
using namespace esphome::cover; using namespace esphome::cover;
static ProgmemStr cover_state_to_mqtt_str(CoverOperation operation, float position, bool supports_position) {
if (operation == COVER_OPERATION_OPENING)
return ESPHOME_F("opening");
if (operation == COVER_OPERATION_CLOSING)
return ESPHOME_F("closing");
if (position == COVER_CLOSED)
return ESPHOME_F("closed");
if (position == COVER_OPEN)
return ESPHOME_F("open");
if (supports_position)
return ESPHOME_F("open");
return ESPHOME_F("unknown");
}
MQTTCoverComponent::MQTTCoverComponent(Cover *cover) : cover_(cover) {} MQTTCoverComponent::MQTTCoverComponent(Cover *cover) : cover_(cover) {}
void MQTTCoverComponent::setup() { void MQTTCoverComponent::setup() {
auto traits = this->cover_->get_traits(); auto traits = this->cover_->get_traits();
@@ -109,14 +124,10 @@ bool MQTTCoverComponent::publish_state() {
if (!this->publish(this->get_tilt_state_topic(), pos, len)) if (!this->publish(this->get_tilt_state_topic(), pos, len))
success = false; success = false;
} }
const char *state_s = this->cover_->current_operation == COVER_OPERATION_OPENING ? "opening"
: this->cover_->current_operation == COVER_OPERATION_CLOSING ? "closing"
: this->cover_->position == COVER_CLOSED ? "closed"
: this->cover_->position == COVER_OPEN ? "open"
: traits.get_supports_position() ? "open"
: "unknown";
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
if (!this->publish(this->get_state_topic_to_(topic_buf), state_s)) if (!this->publish(this->get_state_topic_to_(topic_buf),
cover_state_to_mqtt_str(this->cover_->current_operation, this->cover_->position,
traits.get_supports_position())))
success = false; success = false;
return success; return success;
} }

View File

@@ -1,5 +1,6 @@
#include "mqtt_fan.h" #include "mqtt_fan.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "mqtt_const.h" #include "mqtt_const.h"
@@ -12,6 +13,14 @@ static const char *const TAG = "mqtt.fan";
using namespace esphome::fan; using namespace esphome::fan;
static ProgmemStr fan_direction_to_mqtt_str(FanDirection direction) {
return direction == FanDirection::FORWARD ? ESPHOME_F("forward") : ESPHOME_F("reverse");
}
static ProgmemStr fan_oscillation_to_mqtt_str(bool oscillating) {
return oscillating ? ESPHOME_F("oscillate_on") : ESPHOME_F("oscillate_off");
}
MQTTFanComponent::MQTTFanComponent(Fan *state) : state_(state) {} MQTTFanComponent::MQTTFanComponent(Fan *state) : state_(state) {}
Fan *MQTTFanComponent::get_state() const { return this->state_; } Fan *MQTTFanComponent::get_state() const { return this->state_; }
@@ -164,13 +173,12 @@ bool MQTTFanComponent::publish_state() {
this->publish(this->get_state_topic_to_(topic_buf), state_s); this->publish(this->get_state_topic_to_(topic_buf), state_s);
bool failed = false; bool failed = false;
if (this->state_->get_traits().supports_direction()) { if (this->state_->get_traits().supports_direction()) {
bool success = this->publish(this->get_direction_state_topic(), bool success = this->publish(this->get_direction_state_topic(), fan_direction_to_mqtt_str(this->state_->direction));
this->state_->direction == fan::FanDirection::FORWARD ? "forward" : "reverse");
failed = failed || !success; failed = failed || !success;
} }
if (this->state_->get_traits().supports_oscillation()) { if (this->state_->get_traits().supports_oscillation()) {
bool success = this->publish(this->get_oscillation_state_topic(), bool success =
this->state_->oscillating ? "oscillate_on" : "oscillate_off"); this->publish(this->get_oscillation_state_topic(), fan_oscillation_to_mqtt_str(this->state_->oscillating));
failed = failed || !success; failed = failed || !success;
} }
auto traits = this->state_->get_traits(); auto traits = this->state_->get_traits();

View File

@@ -1,5 +1,6 @@
#include "mqtt_valve.h" #include "mqtt_valve.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "mqtt_const.h" #include "mqtt_const.h"
@@ -12,6 +13,20 @@ static const char *const TAG = "mqtt.valve";
using namespace esphome::valve; using namespace esphome::valve;
static ProgmemStr valve_state_to_mqtt_str(ValveOperation operation, float position, bool supports_position) {
if (operation == VALVE_OPERATION_OPENING)
return ESPHOME_F("opening");
if (operation == VALVE_OPERATION_CLOSING)
return ESPHOME_F("closing");
if (position == VALVE_CLOSED)
return ESPHOME_F("closed");
if (position == VALVE_OPEN)
return ESPHOME_F("open");
if (supports_position)
return ESPHOME_F("open");
return ESPHOME_F("unknown");
}
MQTTValveComponent::MQTTValveComponent(Valve *valve) : valve_(valve) {} MQTTValveComponent::MQTTValveComponent(Valve *valve) : valve_(valve) {}
void MQTTValveComponent::setup() { void MQTTValveComponent::setup() {
auto traits = this->valve_->get_traits(); auto traits = this->valve_->get_traits();
@@ -78,14 +93,10 @@ bool MQTTValveComponent::publish_state() {
if (!this->publish(this->get_position_state_topic(), pos, len)) if (!this->publish(this->get_position_state_topic(), pos, len))
success = false; success = false;
} }
const char *state_s = this->valve_->current_operation == VALVE_OPERATION_OPENING ? "opening"
: this->valve_->current_operation == VALVE_OPERATION_CLOSING ? "closing"
: this->valve_->position == VALVE_CLOSED ? "closed"
: this->valve_->position == VALVE_OPEN ? "open"
: traits.get_supports_position() ? "open"
: "unknown";
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
if (!this->publish(this->get_state_topic_to_(topic_buf), state_s)) if (!this->publish(this->get_state_topic_to_(topic_buf),
valve_state_to_mqtt_str(this->valve_->current_operation, this->valve_->position,
traits.get_supports_position())))
success = false; success = false;
return success; return success;
} }