MQTT: fan direction control added (#8022)

Co-authored-by: Mateusz Bronk <mbronk@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Mateusz Bronk 2025-05-12 00:28:46 +02:00 committed by GitHub
parent 8757957e17
commit 401c090edd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 65 additions and 0 deletions

View File

@ -5,6 +5,8 @@ from esphome.components import mqtt, web_server
import esphome.config_validation as cv
from esphome.const import (
CONF_DIRECTION,
CONF_DIRECTION_COMMAND_TOPIC,
CONF_DIRECTION_STATE_TOPIC,
CONF_ID,
CONF_MQTT_ID,
CONF_OFF_SPEED_CYCLE,
@ -90,6 +92,12 @@ FAN_SCHEMA = (
RESTORE_MODES, upper=True, space="_"
),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
cv.Optional(CONF_DIRECTION_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_DIRECTION_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
@ -193,6 +201,14 @@ async def setup_fan_core_(var, config):
mqtt_ = cg.new_Pvariable(mqtt_id, var)
await mqtt.register_mqtt_component(mqtt_, config)
if (
direction_state_topic := config.get(CONF_DIRECTION_STATE_TOPIC)
) is not None:
cg.add(mqtt_.set_custom_direction_state_topic(direction_state_topic))
if (
direction_command_topic := config.get(CONF_DIRECTION_COMMAND_TOPIC)
) is not None:
cg.add(mqtt_.set_custom_direction_command_topic(direction_command_topic))
if (
oscillation_state_topic := config.get(CONF_OSCILLATION_STATE_TOPIC)
) is not None:

View File

@ -64,6 +64,8 @@ constexpr const char *const MQTT_DEVICE_NAME = "name";
constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa";
constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw";
constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw";
constexpr const char *const MQTT_DIRECTION_COMMAND_TOPIC = "dir_cmd_t";
constexpr const char *const MQTT_DIRECTION_STATE_TOPIC = "dir_stat_t";
constexpr const char *const MQTT_DOCKED_TEMPLATE = "dock_tpl";
constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t";
constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "fx_cmd_t";
@ -328,6 +330,8 @@ constexpr const char *const MQTT_DEVICE_NAME = "name";
constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area";
constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version";
constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw_version";
constexpr const char *const MQTT_DIRECTION_COMMAND_TOPIC = "direction_command_topic";
constexpr const char *const MQTT_DIRECTION_STATE_TOPIC = "direction_state_topic";
constexpr const char *const MQTT_DOCKED_TEMPLATE = "docked_template";
constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic";
constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "effect_command_topic";

View File

@ -43,6 +43,32 @@ void MQTTFanComponent::setup() {
}
});
if (this->state_->get_traits().supports_direction()) {
this->subscribe(this->get_direction_command_topic(), [this](const std::string &topic, const std::string &payload) {
auto val = parse_on_off(payload.c_str(), "forward", "reverse");
switch (val) {
case PARSE_ON:
ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name().c_str());
this->state_->make_call().set_direction(fan::FanDirection::FORWARD).perform();
break;
case PARSE_OFF:
ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name().c_str());
this->state_->make_call().set_direction(fan::FanDirection::REVERSE).perform();
break;
case PARSE_TOGGLE:
this->state_->make_call()
.set_direction(this->state_->direction == fan::FanDirection::FORWARD ? fan::FanDirection::REVERSE
: fan::FanDirection::FORWARD)
.perform();
break;
case PARSE_NONE:
ESP_LOGW(TAG, "Unknown direction Payload %s", payload.c_str());
this->status_momentary_warning("direction", 5000);
break;
}
});
}
if (this->state_->get_traits().supports_oscillation()) {
this->subscribe(this->get_oscillation_command_topic(),
[this](const std::string &topic, const std::string &payload) {
@ -94,6 +120,10 @@ void MQTTFanComponent::setup() {
void MQTTFanComponent::dump_config() {
ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str());
LOG_MQTT_COMPONENT(true, true);
if (this->state_->get_traits().supports_direction()) {
ESP_LOGCONFIG(TAG, " Direction State Topic: '%s'", this->get_direction_state_topic().c_str());
ESP_LOGCONFIG(TAG, " Direction Command Topic: '%s'", this->get_direction_command_topic().c_str());
}
if (this->state_->get_traits().supports_oscillation()) {
ESP_LOGCONFIG(TAG, " Oscillation State Topic: '%s'", this->get_oscillation_state_topic().c_str());
ESP_LOGCONFIG(TAG, " Oscillation Command Topic: '%s'", this->get_oscillation_command_topic().c_str());
@ -107,6 +137,10 @@ void MQTTFanComponent::dump_config() {
bool MQTTFanComponent::send_initial_state() { return this->publish_state(); }
void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
if (this->state_->get_traits().supports_direction()) {
root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic();
root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic();
}
if (this->state_->get_traits().supports_oscillation()) {
root[MQTT_OSCILLATION_COMMAND_TOPIC] = this->get_oscillation_command_topic();
root[MQTT_OSCILLATION_STATE_TOPIC] = this->get_oscillation_state_topic();
@ -122,6 +156,11 @@ bool MQTTFanComponent::publish_state() {
ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);
this->publish(this->get_state_topic_(), state_s);
bool failed = false;
if (this->state_->get_traits().supports_direction()) {
bool success = this->publish(this->get_direction_state_topic(),
this->state_->direction == fan::FanDirection::FORWARD ? "forward" : "reverse");
failed = failed || !success;
}
if (this->state_->get_traits().supports_oscillation()) {
bool success = this->publish(this->get_oscillation_state_topic(),
this->state_->oscillating ? "oscillate_on" : "oscillate_off");

View File

@ -15,6 +15,8 @@ class MQTTFanComponent : public mqtt::MQTTComponent {
public:
explicit MQTTFanComponent(fan::Fan *state);
MQTT_COMPONENT_CUSTOM_TOPIC(direction, command)
MQTT_COMPONENT_CUSTOM_TOPIC(direction, state)
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command)
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state)
MQTT_COMPONENT_CUSTOM_TOPIC(speed_level, command)

View File

@ -221,7 +221,9 @@ CONF_DIMENSIONS = "dimensions"
CONF_DIO_PIN = "dio_pin"
CONF_DIR_PIN = "dir_pin"
CONF_DIRECTION = "direction"
CONF_DIRECTION_COMMAND_TOPIC = "direction_command_topic"
CONF_DIRECTION_OUTPUT = "direction_output"
CONF_DIRECTION_STATE_TOPIC = "direction_state_topic"
CONF_DISABLE_CRC = "disable_crc"
CONF_DISABLED = "disabled"
CONF_DISABLED_BY_DEFAULT = "disabled_by_default"

View File

@ -293,6 +293,8 @@ fan:
- platform: template
name: Template Fan
state_topic: some/topic/fan
direction_state_topic: some/topic/direction/state
direction_command_topic: some/topic/direction/command
qos: 2
on_state:
- logger.log: on_state