Mqtt topics to support numeric fan speed (#1859)

* numeric speed added

* when dumping config for MQTT components log a note when skipped due to is_internal

* added new topics to paython code validation/generation

* reformatted with black

* formatting corrected

* use dump_config_ mechanism to skip internal components

* use dump_config_ mechanism to skip internal components

* style issues resolved

* do_dump_config removed

* formatting fixed

* formatting fixed

* Drop parent dump_config() calls

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
wifwucite 2021-09-22 13:42:58 +02:00 committed by GitHub
parent e32722db70
commit fd836e982e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 0 deletions

View file

@ -12,6 +12,8 @@ from esphome.const import (
CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_COMMAND_TOPIC,
CONF_OSCILLATION_STATE_TOPIC, CONF_OSCILLATION_STATE_TOPIC,
CONF_SPEED, CONF_SPEED,
CONF_SPEED_LEVEL_COMMAND_TOPIC,
CONF_SPEED_LEVEL_STATE_TOPIC,
CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_COMMAND_TOPIC,
CONF_SPEED_STATE_TOPIC, CONF_SPEED_STATE_TOPIC,
CONF_NAME, CONF_NAME,
@ -57,6 +59,12 @@ FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All( cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic cv.requires_component("mqtt"), cv.subscribe_topic
), ),
cv.Optional(CONF_SPEED_LEVEL_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_SPEED_LEVEL_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All( cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic cv.requires_component("mqtt"), cv.publish_topic
), ),
@ -104,6 +112,18 @@ async def setup_fan_core_(var, config):
config[CONF_OSCILLATION_COMMAND_TOPIC] config[CONF_OSCILLATION_COMMAND_TOPIC]
) )
) )
if CONF_SPEED_LEVEL_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_speed_level_state_topic(
config[CONF_SPEED_LEVEL_STATE_TOPIC]
)
)
if CONF_SPEED_LEVEL_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_speed_level_command_topic(
config[CONF_SPEED_LEVEL_COMMAND_TOPIC]
)
)
if CONF_SPEED_STATE_TOPIC in config: if CONF_SPEED_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC])) cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
if CONF_SPEED_COMMAND_TOPIC in config: if CONF_SPEED_COMMAND_TOPIC in config:

View file

@ -16,6 +16,7 @@ MQTTFanComponent::MQTTFanComponent(FanState *state) : MQTTComponent(), state_(st
FanState *MQTTFanComponent::get_state() const { return this->state_; } FanState *MQTTFanComponent::get_state() const { return this->state_; }
std::string MQTTFanComponent::component_type() const { return "fan"; } std::string MQTTFanComponent::component_type() const { return "fan"; }
void MQTTFanComponent::setup() { void MQTTFanComponent::setup() {
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
auto val = parse_on_off(payload.c_str()); auto val = parse_on_off(payload.c_str());
@ -64,6 +65,26 @@ void MQTTFanComponent::setup() {
}); });
} }
if (this->state_->get_traits().supports_speed()) {
this->subscribe(this->get_speed_level_command_topic(),
[this](const std::string &topic, const std::string &payload) {
optional<int> speed_level_opt = parse_int(payload);
if (speed_level_opt.has_value()) {
const int speed_level = speed_level_opt.value();
if (speed_level >= 0 && speed_level <= this->state_->get_traits().supported_speed_count()) {
ESP_LOGD(TAG, "New speed level %d", speed_level);
this->state_->make_call().set_speed(speed_level).perform();
} else {
ESP_LOGW(TAG, "Invalid speed level %d", speed_level);
this->status_momentary_warning("speed", 5000);
}
} else {
ESP_LOGW(TAG, "Invalid speed level %s (int expected)", payload.c_str());
this->status_momentary_warning("speed", 5000);
}
});
}
if (this->state_->get_traits().supports_speed()) { if (this->state_->get_traits().supports_speed()) {
this->subscribe(this->get_speed_command_topic(), [this](const std::string &topic, const std::string &payload) { this->subscribe(this->get_speed_command_topic(), [this](const std::string &topic, const std::string &payload) {
this->state_->make_call() this->state_->make_call()
@ -75,6 +96,22 @@ void MQTTFanComponent::setup() {
auto f = std::bind(&MQTTFanComponent::publish_state, this); auto f = std::bind(&MQTTFanComponent::publish_state, this);
this->state_->add_on_state_callback([this, f]() { this->defer("send", f); }); this->state_->add_on_state_callback([this, f]() { this->defer("send", f); });
} }
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_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());
}
if (this->state_->get_traits().supports_speed()) {
ESP_LOGCONFIG(TAG, " Speed Level State Topic: '%s'", this->get_speed_level_state_topic().c_str());
ESP_LOGCONFIG(TAG, " Speed Level Command Topic: '%s'", this->get_speed_level_command_topic().c_str());
ESP_LOGCONFIG(TAG, " Speed State Topic: '%s'", this->get_speed_state_topic().c_str());
ESP_LOGCONFIG(TAG, " Speed Command Topic: '%s'", this->get_speed_command_topic().c_str());
}
}
bool MQTTFanComponent::send_initial_state() { return this->publish_state(); } bool MQTTFanComponent::send_initial_state() { return this->publish_state(); }
std::string MQTTFanComponent::friendly_name() const { return this->state_->get_name(); } std::string MQTTFanComponent::friendly_name() const { return this->state_->get_name(); }
void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
@ -83,6 +120,8 @@ void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfi
root["oscillation_state_topic"] = this->get_oscillation_state_topic(); root["oscillation_state_topic"] = this->get_oscillation_state_topic();
} }
if (this->state_->get_traits().supports_speed()) { if (this->state_->get_traits().supports_speed()) {
root["speed_level_command_topic"] = this->get_speed_level_command_topic();
root["speed_level_state_topic"] = this->get_speed_level_state_topic();
root["speed_command_topic"] = this->get_speed_command_topic(); root["speed_command_topic"] = this->get_speed_command_topic();
root["speed_state_topic"] = this->get_speed_state_topic(); root["speed_state_topic"] = this->get_speed_state_topic();
} }
@ -99,6 +138,11 @@ bool MQTTFanComponent::publish_state() {
failed = failed || !success; failed = failed || !success;
} }
auto traits = this->state_->get_traits(); auto traits = this->state_->get_traits();
if (traits.supports_speed()) {
std::string payload = to_string(this->state_->speed);
bool success = this->publish(this->get_speed_level_state_topic(), payload);
failed = failed || !success;
}
if (traits.supports_speed()) { if (traits.supports_speed()) {
const char *payload; const char *payload;
// NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations) // NOLINTNEXTLINE(clang-diagnostic-deprecated-declarations)

View file

@ -17,6 +17,8 @@ class MQTTFanComponent : public mqtt::MQTTComponent {
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command) MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command)
MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state) MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state)
MQTT_COMPONENT_CUSTOM_TOPIC(speed_level, command)
MQTT_COMPONENT_CUSTOM_TOPIC(speed_level, state)
MQTT_COMPONENT_CUSTOM_TOPIC(speed, command) MQTT_COMPONENT_CUSTOM_TOPIC(speed, command)
MQTT_COMPONENT_CUSTOM_TOPIC(speed, state) MQTT_COMPONENT_CUSTOM_TOPIC(speed, state)
@ -26,6 +28,9 @@ class MQTTFanComponent : public mqtt::MQTTComponent {
// (In most use cases you won't need these) // (In most use cases you won't need these)
/// Setup the fan subscriptions and discovery. /// Setup the fan subscriptions and discovery.
void setup() override; void setup() override;
void dump_config() override;
/// Send the full current state to MQTT. /// Send the full current state to MQTT.
bool send_initial_state() override; bool send_initial_state() override;
bool publish_state(); bool publish_state();

View file

@ -597,6 +597,8 @@ CONF_SOURCE = "source"
CONF_SPEED = "speed" CONF_SPEED = "speed"
CONF_SPEED_COMMAND_TOPIC = "speed_command_topic" CONF_SPEED_COMMAND_TOPIC = "speed_command_topic"
CONF_SPEED_COUNT = "speed_count" CONF_SPEED_COUNT = "speed_count"
CONF_SPEED_LEVEL_COMMAND_TOPIC = "speed_level_command_topic"
CONF_SPEED_LEVEL_STATE_TOPIC = "speed_level_state_topic"
CONF_SPEED_STATE_TOPIC = "speed_state_topic" CONF_SPEED_STATE_TOPIC = "speed_state_topic"
CONF_SPI_ID = "spi_id" CONF_SPI_ID = "spi_id"
CONF_SPIKE_REJECTION = "spike_rejection" CONF_SPIKE_REJECTION = "spike_rejection"

View file

@ -1978,6 +1978,8 @@ fan:
direction_output: gpio_26 direction_output: gpio_26
oscillation_state_topic: oscillation/state/topic oscillation_state_topic: oscillation/state/topic
oscillation_command_topic: oscillation/command/topic oscillation_command_topic: oscillation/command/topic
speed_level_state_topic: speed_level/state/topic
speed_level_command_topic: speed_level/command/topic
speed_state_topic: speed/state/topic speed_state_topic: speed/state/topic
speed_command_topic: speed/command/topic speed_command_topic: speed/command/topic
on_speed_set: on_speed_set: