1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-18 07:45:56 +00:00
Files
esphome/esphome/components/mqtt/mqtt_component.cpp
Otto Winter ac0d921413 ESP-IDF support and generic target platforms (#2303)
* Socket refactor and SSL

* esp-idf temp

* Fixes

* Echo component and noise

* Add noise API transport support

* Updates

* ESP-IDF

* Complete

* Fixes

* Fixes

* Versions update

* New i2c APIs

* Complete i2c refactor

* SPI migration

* Revert ESP Preferences migration, too complex for now

* OTA support

* Remove echo again

* Remove ssl again

* GPIOFlags updates

* Rename esphal and ICACHE_RAM_ATTR

* Make ESP32 arduino compilable again

* Fix GPIO flags

* Complete pin registry refactor and fixes

* Fixes to make test1 compile

* Remove sdkconfig file

* Ignore sdkconfig file

* Fixes in reviewing

* Make test2 compile

* Make test4 compile

* Make test5 compile

* Run clang-format

* Fix lint errors

* Use esp-idf APIs instead of btStart

* Another round of fixes

* Start implementing ESP8266

* Make test3 compile

* Guard esp8266 code

* Lint

* Reformat

* Fixes

* Fixes v2

* more fixes

* ESP-IDF tidy target

* Convert ARDUINO_ARCH_ESPxx

* Update WiFiSignalSensor

* Update time ifdefs

* OTA needs millis from hal

* RestartSwitch needs delay from hal

* ESP-IDF Uart

* Fix OTA blank password

* Allow setting sdkconfig

* Fix idf partitions and allow setting sdkconfig from yaml

* Re-add read/write compat APIs and fix esp8266 uart

* Fix esp8266 store log strings in flash

* Fix ESP32 arduino preferences not initialized

* Update ifdefs

* Change how sdkconfig change is detected

* Add checks to ci-custom and fix them

* Run clang-format

* Add esp-idf clang-tidy target and fix errors

* Fixes from clang-tidy idf round 2

* Fixes from compiling tests with esp-idf

* Run clang-format

* Switch test5.yaml to esp-idf

* Implement ESP8266 Preferences

* Lint

* Re-do PIO package version selection a bit

* Fix arduinoespressif32 package version

* Fix unit tests

* Lint

* Lint fixes

* Fix readv/writev not defined

* Fix graphing component

* Re-add all old options from core/config.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-20 11:47:51 +02:00

206 lines
7.3 KiB
C++

#include "mqtt_component.h"
#ifdef USE_MQTT
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/version.h"
namespace esphome {
namespace mqtt {
static const char *const TAG = "mqtt.component";
void MQTTComponent::set_retain(bool retain) { this->retain_ = retain; }
std::string MQTTComponent::get_discovery_topic_(const MQTTDiscoveryInfo &discovery_info) const {
std::string sanitized_name = sanitize_string_allowlist(App.get_name(), HOSTNAME_CHARACTER_ALLOWLIST);
return discovery_info.prefix + "/" + this->component_type() + "/" + sanitized_name + "/" +
this->get_default_object_id_() + "/config";
}
std::string MQTTComponent::get_default_topic_for_(const std::string &suffix) const {
return global_mqtt_client->get_topic_prefix() + "/" + this->component_type() + "/" + this->get_default_object_id_() +
"/" + suffix;
}
const 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_;
}
const 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_;
}
bool MQTTComponent::publish(const std::string &topic, const std::string &payload) {
if (topic.empty())
return false;
return global_mqtt_client->publish(topic, payload, 0, this->retain_);
}
bool MQTTComponent::publish_json(const std::string &topic, const json::json_build_t &f) {
if (topic.empty())
return false;
return global_mqtt_client->publish_json(topic, f, 0, this->retain_);
}
bool MQTTComponent::send_discovery_() {
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
if (discovery_info.clean) {
ESP_LOGV(TAG, "'%s': Cleaning discovery...", this->friendly_name().c_str());
return global_mqtt_client->publish(this->get_discovery_topic_(discovery_info), "", 0, 0, true);
}
ESP_LOGV(TAG, "'%s': Sending discovery...", this->friendly_name().c_str());
return global_mqtt_client->publish_json(
this->get_discovery_topic_(discovery_info),
[this](JsonObject &root) {
SendDiscoveryConfig config;
config.state_topic = true;
config.command_topic = true;
this->send_discovery(root, config);
std::string name = this->friendly_name();
root["name"] = name;
if (config.state_topic)
root["state_topic"] = this->get_state_topic_();
if (config.command_topic)
root["command_topic"] = this->get_command_topic_();
if (this->availability_ == nullptr) {
if (!global_mqtt_client->get_availability().topic.empty()) {
root["availability_topic"] = global_mqtt_client->get_availability().topic;
if (global_mqtt_client->get_availability().payload_available != "online")
root["payload_available"] = global_mqtt_client->get_availability().payload_available;
if (global_mqtt_client->get_availability().payload_not_available != "offline")
root["payload_not_available"] = global_mqtt_client->get_availability().payload_not_available;
}
} else if (!this->availability_->topic.empty()) {
root["availability_topic"] = this->availability_->topic;
if (this->availability_->payload_available != "online")
root["payload_available"] = this->availability_->payload_available;
if (this->availability_->payload_not_available != "offline")
root["payload_not_available"] = this->availability_->payload_not_available;
}
const std::string &node_name = App.get_name();
std::string unique_id = this->unique_id();
if (!unique_id.empty()) {
root["unique_id"] = unique_id;
} else {
// default to almost-unique ID. It's a hack but the only way to get that
// gorgeous device registry view.
root["unique_id"] = "ESP" + this->component_type() + this->get_default_object_id_();
}
JsonObject &device_info = root.createNestedObject("device");
device_info["identifiers"] = get_mac_address();
device_info["name"] = node_name;
device_info["sw_version"] = "esphome v" ESPHOME_VERSION " " + App.get_compilation_time();
device_info["model"] = ESPHOME_BOARD;
device_info["manufacturer"] = "espressif";
},
0, discovery_info.retain);
}
bool MQTTComponent::get_retain() const { return this->retain_; }
bool MQTTComponent::is_discovery_enabled() const {
return this->discovery_enabled_ && global_mqtt_client->is_discovery_enabled();
}
std::string MQTTComponent::get_default_object_id_() const {
return sanitize_string_allowlist(to_lowercase_underscore(this->friendly_name()), HOSTNAME_CHARACTER_ALLOWLIST);
}
void MQTTComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) {
global_mqtt_client->subscribe(topic, std::move(callback), qos);
}
void MQTTComponent::subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos) {
global_mqtt_client->subscribe_json(topic, callback, qos);
}
MQTTComponent::MQTTComponent() = default;
float MQTTComponent::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
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;
}
void MQTTComponent::set_custom_command_topic(const std::string &custom_command_topic) {
this->custom_command_topic_ = custom_command_topic;
}
void MQTTComponent::set_availability(std::string topic, std::string payload_available,
std::string payload_not_available) {
this->availability_ = make_unique<Availability>();
this->availability_->topic = std::move(topic);
this->availability_->payload_available = std::move(payload_available);
this->availability_->payload_not_available = std::move(payload_not_available);
}
void MQTTComponent::disable_availability() { this->set_availability("", "", ""); }
void MQTTComponent::call_setup() {
if (this->is_internal())
return;
this->setup();
global_mqtt_client->register_mqtt_component(this);
if (!this->is_connected_())
return;
if (this->is_discovery_enabled()) {
if (!this->send_discovery_()) {
this->schedule_resend_state();
}
}
if (!this->send_initial_state()) {
this->schedule_resend_state();
}
}
void MQTTComponent::call_loop() {
if (this->is_internal())
return;
this->loop();
if (!this->resend_state_ || !this->is_connected_()) {
return;
}
this->resend_state_ = false;
if (this->is_discovery_enabled()) {
if (!this->send_discovery_()) {
this->schedule_resend_state();
}
}
if (!this->send_initial_state()) {
this->schedule_resend_state();
}
}
void MQTTComponent::call_dump_config() {
if (this->is_internal())
return;
this->dump_config();
}
void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; }
std::string MQTTComponent::unique_id() { return ""; }
bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); }
} // namespace mqtt
} // namespace esphome
#endif // USE_MQTT