Browse Source

relay: delay before mqtt disconnect action

https://github.com/xoseperez/espurna/issues/1550#issuecomment-2103315452
wait 30s for reconnection, perform status change after timer expires
pull/2607/head
Maxim Prokhorov 1 month ago
parent
commit
7ac9fd7489
2 changed files with 91 additions and 28 deletions
  1. +4
    -0
      code/espurna/config/general.h
  2. +87
    -28
      code/espurna/relay.cpp

+ 4
- 0
code/espurna/config/general.h View File

@ -454,6 +454,10 @@
#define RELAY_MQTT_DISCONNECT_STATUS RELAY_MQTT_DISCONNECT_NONE
#endif
#ifndef RELAY_MQTT_DISCONNECT_DELAY
#define RELAY_MQTT_DISCONNECT_DELAY duration::Seconds(30)
#endif
//------------------------------------------------------------------------------
// BUTTON
//------------------------------------------------------------------------------


+ 87
- 28
code/espurna/relay.cpp View File

@ -332,6 +332,10 @@ constexpr PayloadStatus mqttDisconnectionStatus(size_t index) {
);
}
constexpr duration::Seconds mqttDisconnectionDelay() {
return RELAY_MQTT_DISCONNECT_DELAY;
}
#endif
} // namespace
@ -408,16 +412,18 @@ PROGMEM_STRING(Mode, "relayPulse");
namespace {
using DurationPair = espurna::settings::internal::duration_convert::Pair;
using ParseResult = espurna::settings::internal::duration_convert::Result;
Duration native_duration(ParseResult result) {
Duration native_duration(DurationPair pair) {
using namespace espurna::settings::internal;
return duration_convert::to_chrono_duration<Duration>(pair);
}
if (result.ok) {
return duration_convert::to_chrono_duration<Duration>(result.value);
}
return Duration::min();
Duration native_duration(ParseResult result) {
return result.ok
? native_duration(result.value)
: Duration::min();
}
ParseResult parse_time(StringView view) {
@ -916,7 +922,8 @@ PROGMEM_STRING(DelayOff, "relayDelayOff");
PROGMEM_STRING(TopicPub, "relayTopicPub");
PROGMEM_STRING(TopicSub, "relayTopicSub");
PROGMEM_STRING(TopicMode, "relayTopicMode");
PROGMEM_STRING(MqttDisconnection, "relayMqttDisc");
PROGMEM_STRING(MqttDelay, "relayMqttDelay");
PROGMEM_STRING(MqttStatus, "relayMqttDisc");
#endif
PROGMEM_STRING(Dummy, "relayDummy");
@ -1022,9 +1029,14 @@ RelayMqttTopicMode mqttTopicMode(size_t index) {
return getSetting({keys::TopicMode, index}, build::mqttTopicMode(index));
}
duration::Seconds mqttDisconnectionDelay() {
return getSetting(keys::MqttDelay, build::mqttDisconnectionDelay());
}
PayloadStatus mqttDisconnectionStatus(size_t index) {
return getSetting({keys::MqttDisconnection, index}, build::mqttDisconnectionStatus(index));
return getSetting({keys::MqttStatus, index}, build::mqttDisconnectionStatus(index));
}
#endif
} // namespace
@ -1068,6 +1080,7 @@ String pulseTime(size_t index) {
}
#if MQTT_SUPPORT
EXACT_VALUE(mqttDisconnectionDelay, settings::mqttDisconnectionDelay)
ID_VALUE(mqttDisconnectionStatus, settings::mqttDisconnectionStatus)
ID_VALUE(mqttTopicMode, settings::mqttTopicMode)
#endif
@ -1078,10 +1091,13 @@ ID_VALUE(mqttTopicMode, settings::mqttTopicMode)
} // namespace internal
static constexpr espurna::settings::query::Setting Settings[] PROGMEM {
{keys::Dummy, internal::dummyCount},
{keys::BootMask, internal::bootMask},
{keys::Dummy, internal::dummyCount},
{keys::Interlock, internal::interlockDelay},
{keys::Sync, internal::syncMode}
#if MQTT_SUPPORT
{keys::MqttDelay, internal::mqttDisconnectionDelay},
#endif
{keys::Sync, internal::syncMode},
};
static constexpr espurna::settings::query::IndexedSetting IndexedSettings[] PROGMEM {
@ -1100,7 +1116,7 @@ static constexpr espurna::settings::query::IndexedSetting IndexedSettings[] PROG
{keys::TopicPub, settings::mqttTopicPub},
{keys::TopicSub, settings::mqttTopicSub},
{keys::TopicMode, internal::mqttTopicMode},
{keys::MqttDisconnection, internal::mqttDisconnectionStatus},
{keys::MqttStatus, internal::mqttDisconnectionStatus},
#endif
};
@ -1360,6 +1376,10 @@ String _relay_payload_toggle;
#endif // MQTT_SUPPORT || API_SUPPORT
#if MQTT_SUPPORT
espurna::timer::SystemTimer _relay_mqtt_timer;
#endif
} // namespace
// -----------------------------------------------------------------------------
@ -1707,25 +1727,30 @@ void _relayProcessPulse(const Relay& relay, size_t id, bool status) {
}
}
// expects generic time input which is converted to float first
// duration equal to 0 would cancel existing timer
// duration greater than 0 would toggle relay and schedule a timer
[[gnu::unused]]
bool _relayHandlePulsePayload(size_t id, espurna::StringView payload) {
void _relayHandlePulseResult(size_t id, espurna::settings::internal::duration_convert::Result result) {
using namespace espurna::relay;
const auto duration = pulse::settings::parse_time(payload);
if (duration.ok) {
const auto native = pulse::settings::native_duration(duration);
if (native.count() == 0) {
pulse::reset(id);
return true;
}
const auto native = pulse::settings::native_duration(result);
if (native.count() == 0) {
pulse::reset(id);
}
pulse::schedule(native, id, relayStatus(id));
relayToggle(id, true, false);
pulse::schedule(native, id, relayStatus(id));
relayToggle(id, true, false);
}
// expects generic time input which is converted to float first
[[gnu::unused]]
bool _relayHandlePulsePayload(size_t id, espurna::StringView payload) {
using namespace espurna::relay;
const auto result = pulse::settings::parse_time(payload);
if (result.ok) {
_relayHandlePulseResult(id, result);
return true;
}
@ -2607,6 +2632,12 @@ private:
RelayMqttTopicMode _mode;
};
void _relayMqttSubscribeBaseTopics() {
mqttSubscribe(MQTT_TOPIC_RELAY "/+");
mqttSubscribe(MQTT_TOPIC_PULSE "/+");
mqttSubscribe(MQTT_TOPIC_LOCK "/+");
}
std::forward_list<RelayCustomTopic> _relay_custom_topics;
void _relayMqttSubscribeCustomTopics() {
@ -2740,13 +2771,44 @@ void _relayMqttHandleCustomTopic(espurna::StringView topic, espurna::StringView
}
}
void _relayMqttHandleDisconnect() {
void _relayMqttHandleDisconnectImmediate() {
using namespace espurna::relay::settings;
for (size_t id = 0; id < _relays.size(); ++id) {
_relayHandleStatus(id, mqttDisconnectionStatus(id));
}
}
void _relayMqttHandleDisconnect() {
using namespace espurna::relay::settings;
const auto delay = mqttDisconnectionDelay();
if (!delay.count()) {
_relayMqttHandleDisconnectImmediate();
return;
}
std::vector<PayloadStatus> relays;
relays.reserve(_relays.size());
for (size_t id = 0; id < _relays.size(); ++id) {
relays.push_back(mqttDisconnectionStatus(id));
}
_relay_mqtt_timer.once(
delay,
[relays]() {
for (size_t id = 0; id < relays.size(); ++id) {
_relayHandleStatus(id, relays[id]);
}
});
}
void _relayMqttHandleConnect() {
_relayMqttSubscribeBaseTopics();
_relayMqttSubscribeCustomTopics();
_relay_mqtt_timer.stop();
}
struct RelayMqttTopicHandler {
using Handler = bool(*)(size_t, espurna::StringView);
espurna::StringView topic;
@ -2772,10 +2834,7 @@ void relayMQTTCallback(unsigned int type, espurna::StringView topic, espurna::St
}
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_RELAY "/+");
mqttSubscribe(MQTT_TOPIC_PULSE "/+");
mqttSubscribe(MQTT_TOPIC_LOCK "/+");
_relayMqttSubscribeCustomTopics();
_relayMqttHandleConnect();
connected = true;
return;
}


Loading…
Cancel
Save