Skip to content

Commit

Permalink
relay: delay before mqtt disconnect action
Browse files Browse the repository at this point in the history
#1550 (comment)
wait 30s for reconnection, perform status change after timer expires
  • Loading branch information
mcspr committed May 16, 2024
1 parent 865c7bf commit 7ac9fd7
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 28 deletions.
4 changes: 4 additions & 0 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
//------------------------------------------------------------------------------
Expand Down
115 changes: 87 additions & 28 deletions code/espurna/relay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ constexpr PayloadStatus mqttDisconnectionStatus(size_t index) {
);
}

constexpr duration::Seconds mqttDisconnectionDelay() {
return RELAY_MQTT_DISCONNECT_DELAY;
}

#endif

} // namespace
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
};

Expand Down Expand Up @@ -1360,6 +1376,10 @@ String _relay_payload_toggle;

#endif // MQTT_SUPPORT || API_SUPPORT

#if MQTT_SUPPORT
espurna::timer::SystemTimer _relay_mqtt_timer;
#endif

} // namespace

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -2607,6 +2632,12 @@ struct RelayCustomTopic {
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() {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down

0 comments on commit 7ac9fd7

Please sign in to comment.