diff --git a/code/espurna/relay.cpp b/code/espurna/relay.cpp index 5912d695..ef3bbec1 100644 --- a/code/espurna/relay.cpp +++ b/code/espurna/relay.cpp @@ -1423,23 +1423,83 @@ const char* relayPayload(PayloadStatus status) { #if MQTT_SUPPORT +namespace { + +// TODO: it *will* handle the duplicates, but we waste memory storing them +// TODO: mqttSubscribe(...) also happens multiple times +// +// this is not really an intended use-case though, but it is techically possible... + +struct RelayCustomTopicBase { + RelayCustomTopicBase() = delete; + RelayCustomTopicBase(const RelayCustomTopicBase&) = delete; + RelayCustomTopicBase(RelayCustomTopicBase&& other) noexcept : + _value(std::move(other._value)), + _mode(other._mode) + {} + + template + RelayCustomTopicBase(T&& value, RelayMqttTopicMode mode) : + _value(std::forward(value)), + _mode(mode) + {} + + RelayCustomTopicBase& operator=(const char* const value) { + _value = value; + return *this; + } + + RelayCustomTopicBase& operator=(const String& value) { + _value = value; + return *this; + } + + RelayCustomTopicBase& operator=(String&& value) noexcept { + _value = std::move(value); + return *this; + } + + RelayCustomTopicBase& operator=(RelayMqttTopicMode mode) noexcept { + _mode = mode; + return *this; + } + + String&& get() && { + return std::move(_value); + } + + const String& value() const { + return _value; + } + + RelayMqttTopicMode mode() const { + return _mode; + } + +private: + String _value; + RelayMqttTopicMode _mode; +}; struct RelayCustomTopic { RelayCustomTopic() = delete; RelayCustomTopic(const RelayCustomTopic&) = delete; RelayCustomTopic(RelayCustomTopic&&) = delete; - template - RelayCustomTopic(unsigned char id, T&& topic, RelayMqttTopicMode mode) : + RelayCustomTopic(size_t id, RelayCustomTopicBase&& base) : _id(id), - _topic(std::forward(topic)), + _topic(std::move(base).get()), _parts(_topic), - _mode(mode) + _mode(base.mode()) {} unsigned char id() const { return _id; } + const char* const c_str() const { + return _topic.c_str(); + } + const String& topic() const { return _topic; } @@ -1470,28 +1530,15 @@ private: std::forward_list _relay_custom_topics; -// TODO: it *will* handle the duplicates, but we waste memory storing them -// TODO: mqttSubscribe(...) also happens multiple times -// -// this is not really an intended use-case though, but it is techically possible... - void _relayMqttSubscribeCustomTopics() { const size_t relays { relayCount() }; if (!relays) { return; } - struct CustomTopic { - String value; - RelayMqttTopicMode mode; - }; - - std::vector topics; - topics.reserve(relays); - - for (unsigned char id = 0; id < relays; ++id) { - topics[id].value = _relayMqttTopicSub(id); - topics[id].mode = _relayMqttTopicMode(id); + static std::vector topics; + for (size_t id = 0; id < relays; ++id) { + topics.emplace_back(_relayMqttTopicSub(id), _relayMqttTopicMode(id)); } settings::kv_store.foreach([&](settings::kvs_type::KeyValueResult&& kv) { @@ -1512,26 +1559,29 @@ void _relayMqttSubscribeCustomTopics() { if (key.startsWith(SubPrefix)) { if (_relayTryParseId(key.c_str() + strlen(SubPrefix), id)) { - topics[id].value = std::move(kv.value.read()); + topics[id] = std::move(kv.value.read()); } } else if (key.startsWith(ModePrefix)) { using namespace settings::internal; if (_relayTryParseId(key.c_str() + strlen(ModePrefix), id)) { - topics[id].mode = convert(kv.value.read()); + topics[id] = convert(kv.value.read()); } } }); _relay_custom_topics.clear(); for (unsigned char id = 0; id < relays; ++id) { - auto& topic = topics[id]; - if (!topic.value.length()) { + RelayCustomTopicBase& topic = topics[id]; + auto& value = topic.value(); + if (!value.length()) { continue; } - mqttSubscribeRaw(topic.value.c_str()); - _relay_custom_topics.emplace_front(id, std::move(topic.value), topic.mode); + mqttSubscribeRaw(value.c_str()); + _relay_custom_topics.emplace_front(id, std::move(topic)); } + + topics.clear(); } void _relayMqttPublishCustomTopic(unsigned char id) { @@ -1550,6 +1600,8 @@ void _relayMqttPublishCustomTopic(unsigned char id) { mqttSendRaw(topic.c_str(), relayPayload(status)); } +} // namespace + void _relayMqttReport(unsigned char id) { if (id < _relays.size()) { if (_relays[id].report) {