Skip to content

Commit

Permalink
Merge pull request #5026 from tonhuisman/feature/N001-add-support-for…
Browse files Browse the repository at this point in the history
…-custom-subject

[N001] Add support for optional Subject to notify command
  • Loading branch information
TD-er committed May 1, 2024
2 parents e35d205 + 472c17b commit 1a9d871
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 82 deletions.
12 changes: 12 additions & 0 deletions docs/source/Plugin/P000_commands.repl
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,18 @@

``Name,<new name>``"
"
Notify","
:green:`Rules`","
Trigger a notification. Requires the notifier at the numbered index (1..3) to be enabled.

Optionally include the message body (quoted) and subject (quoted) to be passed. NB: The buzzer doesn't support a message body or subject.

Syntax: ``Notify,<notifier>[,<message>[,<subject>]]``

Example:

``Notify,1,'The temperature is currently [ds#temp] degrees!','Temperature warning'``"
"
NTPHost[,<host-name_or_ip>]","
:red:`Internal`","
Set the name of the NTP-host
Expand Down
113 changes: 54 additions & 59 deletions src/_N001_Email.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
// #######################################################################################################

/** Changelog:
* 2024-04-06 tonhuisman: Add support for (also) supplying a custom subject when sending an email notification via the notify command
* Log reply from mailserver at DEBUG level
* Code improvements
* 2022-12-29 tonhuisman: Add Date: field to email header to reduce spam score, see https://github.com/letscontrolit/ESPEasy/issues/3865
* 2022-12-29 tonhuisman: Start changelog
*/
*/

# define NPLUGIN_001
# define NPLUGIN_ID_001 1
Expand Down Expand Up @@ -42,7 +45,7 @@ bool NPlugin_001_Auth(WiFiClient & client,
const String& pass);
bool NPlugin_001_MTA(WiFiClient & client,
const String& aStr,
uint16_t aWaitForPattern);
uint16_t aWaitForPattern);
bool getNextMailAddress(const String& data,
String & address,
int index);
Expand Down Expand Up @@ -91,13 +94,14 @@ bool NPlugin_001(NPlugin::Function function, struct EventStruct *event, String&
LoadNotificationSettings(event->NotificationIndex, (uint8_t *)&NotificationSettings, sizeof(NotificationSettingsStruct));
NotificationSettings.validate();
String subject = NotificationSettings.Subject;
String body;
String body = NotificationSettings.Body;

if (event->String1.length() > 0) {
if (!event->String1.isEmpty()) {
body = event->String1;
}
else {
body = NotificationSettings.Body;

if (!event->String2.isEmpty()) {
subject = event->String2;
}
subject = parseTemplate(subject);
body = parseTemplate(body);
Expand All @@ -120,31 +124,32 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co
// Use WiFiClient class to create TCP connections
WiFiClient client;

# ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
# ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS

// See: https://github.com/espressif/arduino-esp32/pull/6676
client.setTimeout((CONTROLLER_CLIENTTIMEOUT_MAX + 500) / 1000); // in seconds!!!!
Client *pClient = &client;
pClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX);
# else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
# else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
client.setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); // in msec as it should be!
# endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
# endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS

# ifndef BUILD_NO_DEBUG

#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
addLog(LOG_LEVEL_DEBUG, strformat(
F("EMAIL: Connecting to %s:%d"),
notificationsettings.Server,
notificationsettings.Port));
F("EMAIL: Connecting to %s:%d"),
notificationsettings.Server,
notificationsettings.Port));
}
#endif
# endif // ifndef BUILD_NO_DEBUG

if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, CONTROLLER_CLIENTTIMEOUT_DFLT)) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
addLog(LOG_LEVEL_ERROR, strformat(
F("EMAIL: Error connecting to %s:%d"),
notificationsettings.Server,
notificationsettings.Port));
F("EMAIL: Error connecting to %s:%d"),
notificationsettings.Server,
notificationsettings.Port));
}
myStatus = false;
} else {
Expand All @@ -159,27 +164,22 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co
"X-Mailer: EspEasy v$espeasyversion\r\n\r\n"
);

String email_address = notificationsettings.Sender;
int pos_less = email_address.indexOf('<');

if (pos_less == -1) {
// No email address markup
mailheader.replace(F("$nodename"), Settings.getHostname());
mailheader.replace(F("$emailfrom"), notificationsettings.Sender);
} else {
String senderName = email_address.substring(0, pos_less);
removeChar(senderName, '"'); // Remove quotes
String address = email_address.substring(pos_less + 1);
removeChar(address, '<');
removeChar(address, '>');
address.trim();
String email_address(notificationsettings.Sender);
int pos_less = email_address.indexOf('<');
String senderName = Settings.getHostname();

if (pos_less > -1) {
senderName = email_address.substring(0, pos_less);
removeChar(senderName, '"'); // Remove quotes
email_address = email_address.substring(pos_less + 1);
removeChar(email_address, '<');
removeChar(email_address, '>');
email_address.trim();
senderName.trim();
mailheader.replace(F("$nodename"), senderName);
mailheader.replace(F("$emailfrom"), address);
}

mailheader.replace(F("$nodename"), Settings.getHostname());
mailheader.replace(F("$emailfrom"), notificationsettings.Sender);
mailheader.replace(F("$nodename"), senderName);
mailheader.replace(F("$emailfrom"), email_address);
mailheader.replace(F("$ato"), notificationsettings.Receiver);
mailheader.replace(F("$subject"), aSub);
String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%");
Expand All @@ -196,16 +196,17 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co
while (true) {
if (!NPlugin_001_MTA(client, EMPTY_STRING, 220)) { break; }

if (!NPlugin_001_MTA(client, concat(F("EHLO "), String(notificationsettings.Domain)), 250)) { break; }
if (!NPlugin_001_MTA(client, strformat(F("EHLO %s"), notificationsettings.Domain), 250)) { break; }

if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass)) { break; }

if (!NPlugin_001_MTA(client, concat(F("MAIL FROM:<"), concat(notificationsettings.Sender, '>')) , 250)) { break; }
if (!NPlugin_001_MTA(client, strformat(F("MAIL FROM:<%s>"), email_address.c_str()), 250)) { break; }

bool nextAddressAvailable = true;
int i = 0;
String emailTo;
const String receiver(notificationsettings.Receiver);

if (!getNextMailAddress(receiver, emailTo, i)) {
addLog(LOG_LEVEL_ERROR, F("Email: No recipient given"));
break;
Expand All @@ -216,16 +217,18 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co
addLogMove(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo));
}

if (!NPlugin_001_MTA(client, concat(F("RCPT TO:<"), emailTo + '>'), 250)) { break; }
if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250)) { break; }
++i;
nextAddressAvailable = getNextMailAddress(receiver, emailTo, i);
}

if (!NPlugin_001_MTA(client, F("DATA"), 354)) { break; }

if (!NPlugin_001_MTA(client, mailheader + aMesg + F("\r\n.\r\n"), 250)) { break; }
if (!NPlugin_001_MTA(client, strformat(F("%s%s\r\n.\r\n"), mailheader.c_str(), aMesg.c_str()), 250)) { break; }

myStatus = true;

NPlugin_001_MTA(client, F("QUIT"), 221); // Sent successfully, close SMTP protocol, ignore failure
break;
}

Expand All @@ -250,18 +253,17 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass
return true;
}
base64 encoder;

return NPlugin_001_MTA(client, F("AUTH LOGIN"), 334) &&
NPlugin_001_MTA(client, encoder.encode(user), 334) &&
NPlugin_001_MTA(client, encoder.encode(pass), 235);
}

bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern)
{
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
addLog(LOG_LEVEL_DEBUG, aStr);
}
#endif
# ifndef BUILD_NO_DEBUG
addLog(LOG_LEVEL_DEBUG, aStr);
# endif // ifndef BUILD_NO_DEBUG

if (aStr.length()) { client.println(aStr); }

Expand All @@ -270,36 +272,29 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa

backgroundtasks();

const String aWaitForPattern_str = String(aWaitForPattern) + ' ';
const String aWaitForPattern_str = strformat(F("%d "), aWaitForPattern);

while (true) {
if (timeOutReached(timer)) {
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
addLogMove(LOG_LEVEL_ERROR, concat(
F("NPlugin_001_MTA: timeout. "),
aStr));
addLogMove(LOG_LEVEL_ERROR,
concat(F("NPlugin_001_MTA: timeout. "), aStr));
}
return false;
break;
}

delay(0);

// String line = client.readStringUntil('\n');
String line;
safeReadStringUntil(client, line, '\n');

const bool patternFound = line.indexOf(aWaitForPattern_str) >= 0;

# ifndef BUILD_NO_DEBUG
# ifndef BUILD_NO_DEBUG
addLogMove(LOG_LEVEL_DEBUG, line);
# endif // ifndef BUILD_NO_DEBUG

if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
addLogMove(LOG_LEVEL_DEBUG, line);
}
# endif // ifndef BUILD_NO_DEBUG

if (patternFound) {
return true;
}
return patternFound;
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/src/Commands/InternalCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ bool InternalCommands::executeInternalCommand()
case ESPEasy_cmd_e::name: COMMAND_CASE_R(Command_Settings_Name, 1); // Settings.h
case ESPEasy_cmd_e::nosleep: COMMAND_CASE_R(Command_System_NoSleep, 1); // System.h
#if FEATURE_NOTIFIER
case ESPEasy_cmd_e::notify: COMMAND_CASE_R(Command_Notifications_Notify, 2); // Notifications.h
case ESPEasy_cmd_e::notify: COMMAND_CASE_R(Command_Notifications_Notify, -1); // Notifications.h
#endif // if FEATURE_NOTIFIER
case ESPEasy_cmd_e::ntphost: COMMAND_CASE_R(Command_NTPHost, 1); // Time.h
#ifdef USES_P019
Expand Down
3 changes: 3 additions & 0 deletions src/src/Commands/Notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
const __FlashStringHelper * Command_Notifications_Notify(struct EventStruct *event, const char* Line)
{
String message;
String subject;
GetArgv(Line, message, 3);
GetArgv(Line, subject, 4);

if (event->Par1 > 0) {
int index = event->Par1 - 1;
Expand All @@ -27,6 +29,7 @@ const __FlashStringHelper * Command_Notifications_Notify(struct EventStruct *eve
// TempEvent.NotificationProtocolIndex = NotificationProtocolIndex;
TempEvent.NotificationIndex = index;
TempEvent.String1 = message;
TempEvent.String2 = subject;
Scheduler.schedule_notification_event_timer(NotificationProtocolIndex, NPlugin::Function::NPLUGIN_NOTIFY, std::move(TempEvent));
}
}
Expand Down

0 comments on commit 1a9d871

Please sign in to comment.