Hi,
Ich nutze gerne CURL (https://curl.se/) zum Senden von E-Mails, da es eigentlich in jeder Distro verfügbar ist und sehr viele moderne Optionen beherrscht. Hier ein FHEM-Device, dass man einfach nur anlegt und dann kann man seine E-Mails so versenden:
- set sendMail message Mein Text
- set sendMail message Betreff="Mein Betreff" Mein Text
- set sendMail message Subject="Dies ist ein Test" Mein Text
- set sendMail message Subject="✅ Dies ist ein Test" Meldung mit Emoji ⚽ geht auch
defmod sendMail dummy
attr sendMail readingList message
attr sendMail setList message
attr sendMail userReadings result:message:.* {\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)="(.*?)"//) {\
$subject = $1;;\
}\
\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@domain.com';;\
my $emailPass = 'geheimesPasswort';;\
my $emailServer = 'mail.smtpserver.de';;\
\
my $cmd = "echo 'Subject: $subject\\r\\n";;\
$cmd .= "From: $emailFrom\\r\\n";;\
$cmd .= "To: $emailTo\\r\\n";;\
$cmd .= "Content-Type: text/plain;; charset=UTF-8\\r\\n";;\
$cmd .= "\\r\\n";;\
$cmd .= "$message' | ";;\
\
$cmd .= "curl --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent successfully with status %{http_code}\n' ";;\
$cmd .= "--upload-file - 2>&1";;\
\
return qx/$cmd/;;\
}
Im Wiki sind weitere Methoden, die mir allerdings nicht so zusagten: https://wiki.fhem.de/wiki/E-Mail_senden
Anmerkungen:
- qx blockiert afaik FHEM für die Ausführungsszeit, was ein Problem sein könnte, falls der Server nicht erreichbar ist.
- für sowas würde ich setKeyValue() und getKeyValue() verwenden, dann kann man die ganzen Zugangsdaten einmalig wegspeichern und sie sind nicht über FHEMWEB ohne nähere Kenntnisse des Mechanismus auslesbar; weiter könnte man so auch mehrere unterschiedliche Empfänger möglich machen...
Konsequenterweise sollte man auch den Empfänger mit angeben können, dies habe ich noch ergänzt und die Wiki Seite aktualisiert:
defmod sendMail dummy
attr sendMail readingList message
attr sendMail setList message
attr sendMail userReadings result:message:.* {\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@domain.com';;\
my $emailPass = 'geheimesPasswort';;\
my $emailServer = 'mail.smtpserver.de';;\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)="(.*?)"//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)="(.*?)"//) {\
$emailTo = $1;;\
}\
\
my $cmd = "echo 'Subject: $subject\\r\\n";;\
$cmd .= "From: $emailFrom\\r\\n";;\
$cmd .= "To: $emailTo\\r\\n";;\
$cmd .= "Content-Type: text/plain;; charset=UTF-8\\r\\n";;\
$cmd .= "\\r\\n";;\
$cmd .= "$message' | ";;\
\
$cmd .= "curl --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent successfully with status %{http_code}\n' ";;\
$cmd .= "--upload-file - 2>&1";;\
\
return qx/$cmd/;;\
}
Zitat von: Beta-User am 18 Februar 2025, 08:23:42Anmerkungen:
- qx blockiert afaik FHEM für die Ausführungsszeit, was ein Problem sein könnte, falls der Server nicht erreichbar ist.
- für sowas würde ich setKeyValue() und getKeyValue() verwenden, dann kann man die ganzen Zugangsdaten einmalig wegspeichern und sie sind nicht über FHEMWEB ohne nähere Kenntnisse des Mechanismus auslesbar; weiter könnte man so auch mehrere unterschiedliche Empfänger möglich machen...
Bezüglich des Blockieren bietet cURL die Option --max-time an: https://curl.se/docs/manpage.html#-m
Ich denke eine kurze maximale Zeit von zwei Sekunden sollte FHEM nicht umbringen und solch eine Textemail sollte in der Zeit eigentlich immer gesendet worden sein.
Bezüglich setKeyValue() und getKeyValue() verstehe ich die Absicht, bin aber vom Sicherheitsgewinn nicht so überzeugt. Ich guck' mal...
Zitat von: Torxgewinde am 18 Februar 2025, 08:52:31eine kurze maximale Zeit von zwei Sekunden sollte FHEM nicht umbringen und solch eine Textemail sollte in der Zeit eigentlich immer gesendet worden sein.
Wenn andere Personen wie Administratoren 2 Sekunden warten müssen, bis das Licht angeht, ist das nach meoner persönlichen Erfahrung ein nogo ;) .
Und es geht auch nicht primär nur um die Datenmenge, sondern - wie bereits geschrieben - um die Erreichbarkeit des (E-Mail-) Servers...
Zitat von: Beta-User am 18 Februar 2025, 08:58:19Zitat von: Torxgewinde am 18 Februar 2025, 08:52:31eine kurze maximale Zeit von zwei Sekunden sollte FHEM nicht umbringen und solch eine Textemail sollte in der Zeit eigentlich immer gesendet worden sein.
Wenn andere Personen wie Administratoren 2 Sekunden warten müssen, bis das Licht angeht, ist das nach meoner persönlichen Erfahrung ein nogo ;) .
Und es geht auch nicht primär nur um die Datenmenge, sondern - wie bereits geschrieben - um die Erreichbarkeit des (E-Mail-) Servers...
Mit "-m" sagt man CURL ja nicht "blockiere bitte für mindestens N Sekunden" und
qx() verbessert hier die Lesbarkeit. Ich habe mir den Punkt überlegt und baue "-m" mal ein, das macht Sinn, das
qx() zu entfernen überzeugt mich jetzt aber erstmal nicht.
Das Passwort per
get/setKeyValue() macht Sinn, das kann da auch noch rein.
Hier wäre es nun mit den angeregten Änderungen:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message password
attr sendMail userReadings result:message:.* {\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@domain.com';;\
my $emailServer = 'mail.smtpserver.de';;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)="(.*?)"//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)="(.*?)"//) {\
$emailTo = $1;;\
}\
\
#my $cmd = "echo 'Subject: $subject\\nFrom: $emailFrom\\nTo: $emailTo\\n\\n$message' | ";;\
my $cmd = "echo 'Subject: $subject\\r\\n";;\
$cmd .= "From: $emailFrom\\r\\n";;\
$cmd .= "To: $emailTo\\r\\n";;\
$cmd .= "Content-Type: text/plain;; charset=UTF-8\\r\\n";;\
$cmd .= "\\r\\n";;\
$cmd .= "$message' | ";;\
\
$cmd .= "curl -m 2.0 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";;\
$cmd .= "--upload-file - 2>&1";;\
\
return qx/$cmd/;;\
},\
result:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Das Passwort setzt man dann mit:
- set sendMail password geheimesPasswort
- Alternativ, damit das Passwort nicht im Log auftaucht: { setKeyValue("sendMail_password", "geheimesPasswort") }
Ich habe nicht alles genau verfolgt, aber Shell Befehle kann man in Fhem mit
"echo nonblocking"
nicht blockierend ausführen. Innerhalb von Perl Code ist es etwas komplizierter, geht aber auch:
{fhem("\"echo nonblocking\"")}
Wenn du natürlich die Rückgabe wirklich brauchst, dann wirds komplizierter, geht aber meines Wissens auch, wenn du quasi von außen den Rückgabewert wieder an Fhem übergibst:
perl fhem.pl 7073 "set sendMail result $myRueckgabe"
Irgendwie so in der Art glaube ich mich zu erinnern...
tl;dr: Klar, kannst du einfach machen wenn du den Rückgabewert nicht im Reading haben willst. Der Output ist ja immerhin im Log.
Otto hat das in seinem Blog schön zusammengefasst: https://heinz-otto.blogspot.com/2018/02/in-fhem-externe-programme-aufrufen.html
Ich habe ein simples Beispiel für BlockingCall() erstellt, das kann man hierauf adaptieren: https://forum.fhem.de/index.php?topic=140846.0, aber so richtig nötig ist es hier nicht.
Moin,
Ich hab' es nun doch einfach mal mit BlockingCall() zusammengebaut:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message password
attr sendMail userReadings state:message:.* {\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@example.com';;\
my $emailServer = 'mail.smtpserver.de';;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)="(.*?)"//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)="(.*?)"//) {\
$emailTo = $1;;\
}\
\
my $cmd = "echo 'Subject: $subject\\r\\n";;\
$cmd .= "From: $emailFrom\\r\\n";;\
$cmd .= "To: $emailTo\\r\\n";;\
$cmd .= "Content-Type: text/plain;; charset=UTF-8\\r\\n";;\
$cmd .= "\\r\\n";;\
$cmd .= "$message' | ";;\
\
$cmd .= "curl -m 4.8 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";;\
$cmd .= "--upload-file - 2>&1";;\
\
no warnings 'redefine';;\
sub sendMailfunc1($) {\
my ($param) = @_;;\
my $result;;\
\
$result = qx/$param/;;\
\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
};;\
\
sub sendMailfunc2($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
};;\
use warnings 'redefine';;\
\
BlockingCall("sendMailfunc1", $cmd,"sendMailfunc2", 5);;\
\
return "started Job...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Hallo,
Damit auch der letzte Vorteil von dem veraltetem sendemail bzw. DebianMail Aufruf gleichzieht, habe ich den Versand von Dateianhängen noch ergänzt:
- set sendMail message Mein Text
- set sendMail message Betreff="Mein Betreff" Mein Text
- set sendMail message Subject="Dies ist ein Test" Mein Text
- set sendMail message Subject="✅ Dies ist ein Test" Meldung mit Emoji ⚽ geht auch
- Ein Anhang: set sendMail message Subject="Dies ist ein Test" Mein Text Anhang="/opt/fhem/www/images/fhemSVG/bag.svg"
- Mehrere Anhänge geht auch: Anhang="www/images/fhemSVG/bag.svg" Anhang="www/images/fhemSVG/batterie.svg" To="me@example.com" Subject="✅ Dies ist ein Test" Meldung mit Emoji ⚽ geht auch
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message password
attr sendMail userReadings state:message:.* {\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@example.org';;\
my $emailServer = 'mail.smtpserver.de';;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)="(.*?)"//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)="(.*?)"//) {\
$emailTo = $1;;\
}\
\
#Dateianhänge bestimmen\
my @attachments;;\
while ($message =~ s/(?:Attachment|Anhang)="(.*?)"//) {\
my $file = $1;;\
return "$file not found, not sending email" unless (-e $file);;\
push (@attachments, $1);;\
}\
\
#der curl Befehl wird hier aufgebaut:\
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";;\
$cmd .= "-H 'Subject: $subject' ";;\
$cmd .= "-H 'From: $emailFrom' ";;\
\
#multipart/alternative für Text und HTML EMail Body\
$cmd .= "-F '=(;;type=multipart/alternative' ";;\
$cmd .= "-F '=$message;;type=text/plain;; charset=UTF-8' ";;\
$cmd .= "-F '= <body>$message</body>;;type=text/html;; charset=UTF-8' ";;\
$cmd .= "-F '=)' ";;\
\
#jetzt alle Attachments anhängen:\
foreach my $file (@attachments) {\
$cmd .= "-F '=@".$file.";;encoder=base64' ";;\
}\
\
#stderr auch mit in den output sammeln:\
$cmd .= " 2>&1";;\
\
#blocking function that is called from a forked process\
if (!defined &sendMailfunc1) {\
*sendMailfunc1 = sub ($) {\
my ($param) = @_;;\
my $result;;\
$result = qx/$param/;;\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
}\
}\
\
#finish function that is called when blocking function is done\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20);;\
\
return "started Job...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Hallo Torxgewinde,
tolle Alternative zum DebianMail Aufruf - danke!
Ich wollte eine mehrzeilige Eingabe für den Message-Text machen und habe daher wie folgt ergänzt:
attr sendMail setList message:textField-long
Im userReading habe ich nach dem Einlesen der message wie folgt ergänzt:
$message =~ s/\r?\n/<br>/g;
Sobald ich aber einen Pagebreak einfüge, wird das userReading nicht mehr getriggert, obwohl im Event monitor ein Event angezeigt wird.
set sendMail message An="test.tester@gmail.com" Hallo Test,hier ein kleiner Test!Lg Tester
Event monitor: 2025-02-26 15:46:30 dummy sendMail message: An="test.tester@gmail.com" Hallo Test, hier ein kleiner Test! Lg Tester
Gibt es dafür eine Erklärung?
Danke, Robert
Moin!
Das hat mich gerade ziemlich stutzig gemacht, aber du hast recht. Es scheint daran zu liegen, dass der Trigger, wodurch das userReading aktiv wird, nicht mit den Umbrüchen klarkommt und nicht triggert. Es liegt meiner Meinung nach daran, dass der Regex .* nicht auf \n und so weiter anspricht, dies hier aber notwendig ist um bis zum Zeilenende zu kommen.
Ich kann dir folgendes zum Test mit der RegEx [\s\S]* anbieten, bei mir klappt es damit:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message:textField-long password
attr sendMail userReadings state:message:[\s\S]* {\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@exmaple.org';;\
my $emailServer = 'mail.smtpserver.de';;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)="(.*?)"//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)="(.*?)"//) {\
$emailTo = $1;;\
}\
\
#Dateianhänge bestimmen\
my @attachments;;\
while ($message =~ s/(?:Attachment|Anhang)="(.*?)"//) {\
my $file = $1;;\
return "$file not found, not sending email" unless (-e $file);;\
push (@attachments, $1);;\
}\
\
#der curl Befehl wird hier aufgebaut:\
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";;\
$cmd .= "-H 'Subject: $subject' ";;\
$cmd .= "-H 'From: $emailFrom' ";;\
\
#multipart/alternative für Text und HTML EMail Body\
$cmd .= "-F '=(;;type=multipart/alternative' ";;\
$cmd .= "-F '=$message;;type=text/plain;; charset=UTF-8' ";;\
$message =~ s/\r?\n/<br \/>/g;;\
$cmd .= "-F '= <body>$message</body>;;type=text/html;; charset=UTF-8' ";;\
$cmd .= "-F '=)' ";;\
\
#jetzt alle Attachments anhängen:\
foreach my $file (@attachments) {\
$cmd .= "-F '=@".$file.";;encoder=base64' ";;\
}\
\
#stderr auch mit in den output sammeln:\
$cmd .= " 2>&1";;\
\
#blocking function that is called from a forked process\
if (!defined &sendMailfunc1) {\
*sendMailfunc1 = sub ($) {\
my ($param) = @_;;\
my $result;;\
$result = qx/$param/;;\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
}\
}\
\
#finish function that is called when blocking function is done\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20);;\
\
return "started Job...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Perfekt, so funktioniert es!
Danke für die rasche Hilfe :)
Vielleicht magst du meine Adaptierungen in deinem Codeschnipsel aufnehmen.
Dann würde der Codeschnipsel für ein und mehrzeilige sendMail funnktionieren und
auch die Verwendung von einfachen oder doppelten Hochkomma berücksichtigen.
Mehrzeilige Eingabe und auch Triggern des Events.
attr sendMail setList message:textField-long
attr sendMail userReadings state:message:[\s\S]* {\
Zeilenumbruch mit dem html <br> ersetzen.
my $message = ReadingsVal($name, 'message', '???');;\
$message =~ s/\r?\n/<br>/g;;\
Egal ob jemand beim sendMail " (doppeltes Hochkomma) oder ' (einfaches Hochkomma) nimmt, beides wird richtig abgehandelt.
if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {\
if ($message =~ s/(?:To|An)=["'](.*?)["']//) {\
while ( $message =~ s/(?:Attachment|Anhang)=["'](.*?)["']// ) {\
Leerzeichen und <br> am Anfang und Ende vom Messagetext entfernen.
# Leerzeichen und <br> am Anfang entfernen\
$message =~ s/^(\s|<br>)+//;;\
# Leerzeichen und <br> am Ende entfernen\
$message =~ s/(\s|<br>)+$//;;\
\
# der curl Befehl wird hier aufgebaut:\
Gruß Robert
Hallo Robert,
Klar, dieser Snippet sollte nun die Dinge alle beinhalten. Ich habe auch eine AbortFn() für den Fall eines Timeouts hinzugefügt. Danke für die Hinweise:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message:textField-long password
attr sendMail userReadings state:message:[\s\S]* {\
my $emailTo = 'you@example.com';;\
my $emailFrom = 'fhem@example.org';;\
my $emailServer = 'mail.smtpserver.de';;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)=["'](.*?)["']//) {\
$emailTo = $1;;\
}\
\
#Dateianhänge bestimmen\
my @attachments;;\
while ($message =~ s/(?:Attachment|Anhang)=["'](.*?)["']//) {\
my $file = $1;;\
return ">>$file<< not found, not sending email" unless (-e $file);;\
push (@attachments, $file);;\
}\
\
#der curl Befehl wird hier aufgebaut:\
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";;\
$cmd .= "-H 'Subject: $subject' ";;\
$cmd .= "-H 'From: $emailFrom' ";;\
\
#multipart/alternative für Text und HTML EMail Body\
$cmd .= "-F '=(;;type=multipart/alternative' ";;\
\
# Leerzeichen und <br> bzw <br \> am Anfang entfernen:\
$message =~ s/^(\s|<br(?:[ \/]+)>)+//;;\
# Leerzeichen und <br> bzw <br \> am Ende entfernen:\
$message =~ s/(\s|<br(?:[ \/]+)>)+$//;;\
\
#Plaintext E-Mail Inhalt an cURL:\
$cmd .= "-F \"=$message;;type=text/plain;; charset=UTF-8\" ";;\
#ersetze \n bzw. \r\n durch ein HTML-tag <br />:\
$message =~ s/\r?\n/<br \/>/g;;\
\
#HTML E-Mail Inhalt an cURL:\
$cmd .= "-F '= <body>$message</body>;;type=text/html;; charset=UTF-8' ";;\
$cmd .= "-F '=)' ";;\
\
#jetzt alle Attachments (0..N) anhängen:\
foreach my $file (@attachments) {\
$cmd .= "-F '=@".$file.";;encoder=base64' ";;\
}\
\
#stderr auch mit in den output sammeln:\
$cmd .= " 2>&1";;\
\
#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\
if (!defined &sendMailfunc1) {\
*sendMailfunc1 = sub ($) {\
my ($param) = @_;;\
my $result;;\
$result = qx/$param/;;\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
}\
}\
\
#Rückgabe über diese Funktion,\
#anzeigen in dem reading dieses UserReadings:\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
#funktion falls BlockingCall abgebrochen wird:\
if (!defined &sendMailfunc3) {\
*sendMailfunc3 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20, "sendMailfunc3", "Error: timeout");;\
\
return "started cURL command...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Hallo Torxgewinde,
nachdem mein Email-Client Probleme beim Anzeigen der mit deiner Lösung versendeten Emails hatte (Abrufzeit vom Server statt Zeitpunkt der Emailversendung), habe ich den Email Header um das Feld "Date:" erweitert. Sind nur zwei Zeilen (suche nach emailDate) und funktioniert bei mir optimal.
state:message:[\s\S]* {
my $emailTo = 'you@example.com';
my $emailFrom = 'fhem@example.org';
my $emailServer = 'mail.smtpserver.de';
my $emailDate = strftime "%a, %d %b %Y %H:%M:%S %z", localtime;
# Passwort aus getKeyValue abrufen
my ($err, $emailPass) = getKeyValue("${name}_password");
if ($err || !defined $emailPass) {
return "Error retrieving password: $err";
}
my $message = ReadingsVal($name, 'message', '???');
my $subject = "$name: FHEM Meldung";
# Betreff extrahieren und aus der Nachricht entfernen
if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {
$subject = $1;
}
# Empfänger extrahieren und aus der Nachricht entfernen
if ($message =~ s/(?:To|An)=["'](.*?)["']//) {
$emailTo = $1;
}
#Dateianhänge bestimmen
my @attachments;
while ($message =~ s/(?:Attachment|Anhang)=["'](.*?)["']//) {
my $file = $1;
return ">>$file<< not found, not sending email" unless (-e $file);
push (@attachments, $file);
}
#der curl Befehl wird hier aufgebaut:
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";
$cmd .= "-H 'Subject: $subject' ";
$cmd .= "-H 'From: $emailFrom' ";
$cmd .= "-H 'Date: $emailDate' ";
#multipart/alternative für Text und HTML EMail Body
$cmd .= "-F '=(;type=multipart/alternative' ";
# Leerzeichen und <br> bzw <br \> am Anfang entfernen:
$message =~ s/^(\s|<br(?:[ \/]+)>)+//;
# Leerzeichen und <br> bzw <br \> am Ende entfernen:
$message =~ s/(\s|<br(?:[ \/]+)>)+$//;
#Plaintext E-Mail Inhalt an cURL:
$cmd .= "-F \"=$message;type=text/plain; charset=UTF-8\" ";
#ersetze \n bzw. \r\n durch ein HTML-tag <br />:
$message =~ s/\r?\n/<br \/>/g;
#HTML E-Mail Inhalt an cURL:
$cmd .= "-F '= <body>$message</body>;type=text/html; charset=UTF-8' ";
$cmd .= "-F '=)' ";
#jetzt alle Attachments (0..N) anhängen:
foreach my $file (@attachments) {
$cmd .= "-F '=@".$file.";encoder=base64' ";
}
#stderr auch mit in den output sammeln:
$cmd .= " 2>&1";
#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:
if (!defined &sendMailfunc1) {
*sendMailfunc1 = sub ($) {
my ($param) = @_;
my $result;
$result = qx/$param/;
$result = MIME::Base64::encode_base64($result);
$result =~ s/\n//g;
return $result;
}
}
#Rückgabe über diese Funktion,
#anzeigen in dem reading dieses UserReadings:
if (!defined &sendMailfunc2) {
*sendMailfunc2 = sub ($) {
my ($result) = @_;
my $hash = $defs{$name};
$result = MIME::Base64::decode_base64($result);
readingsSingleUpdate($hash, $reading, $result, 1);
}
}
#funktion falls BlockingCall abgebrochen wird:
if (!defined &sendMailfunc3) {
*sendMailfunc3 = sub ($) {
my ($result) = @_;
my $hash = $defs{$name};
readingsSingleUpdate($hash, $reading, $result, 1);
}
}
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20, "sendMailfunc3", "Error: timeout");
return "started cURL command...";
},
state:password:.* {
# Passwort speichern
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:
#
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }
readingsBulkUpdate($hash, "password", "****");
return "$ret";
}
Gruß
Danny
Hi,
Danke für die Ergänzung, das finde ich gut! Ich habe es auch im Wiki ergänzt.
Als komplettes Device für die Freunde von Copy & Paste:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message:textField-long password
attr sendMail userReadings state:message:[\s\S]* {\
my $emailTo = 'du@example.com';;\
my $emailFrom = 'fhem@example.de';;\
my $emailServer = 'mail.your-server.de';;\
my $emailDate = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)=["'](.*?)["']//) {\
$emailTo = $1;;\
}\
\
#Dateianhänge bestimmen\
my @attachments;;\
while ($message =~ s/(?:Attachment|Anhang)=["'](.*?)["']//) {\
my $file = $1;;\
return ">>$file<< not found, not sending email" unless (-e $file);;\
push (@attachments, $file);;\
}\
\
#der curl Befehl wird hier aufgebaut:\
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}\n' ";;\
$cmd .= "-H 'Subject: $subject' ";;\
$cmd .= "-H 'From: $emailFrom' ";;\
$cmd .= "-H 'Date: $emailDate' ";;\
\
#multipart/alternative für Text und HTML EMail Body\
$cmd .= "-F '=(;;type=multipart/alternative' ";;\
\
# Leerzeichen und <br> bzw <br \> am Anfang entfernen:\
$message =~ s/^(\s|<br(?:[ \/]+)>)+//;;\
# Leerzeichen und <br> bzw <br \> am Ende entfernen:\
$message =~ s/(\s|<br(?:[ \/]+)>)+$//;;\
\
#Plaintext E-Mail Inhalt an cURL:\
$cmd .= "-F \"=$message;;type=text/plain;; charset=UTF-8\" ";;\
#ersetze \n bzw. \r\n durch ein HTML-tag <br />:\
$message =~ s/\r?\n/<br \/>/g;;\
\
#HTML E-Mail Inhalt an cURL:\
$cmd .= "-F '= <body>$message</body>;;type=text/html;; charset=UTF-8' ";;\
$cmd .= "-F '=)' ";;\
\
#jetzt alle Attachments (0..N) anhängen:\
foreach my $file (@attachments) {\
$cmd .= "-F '=@".$file.";;encoder=base64' ";;\
}\
\
#stderr auch mit in den output sammeln:\
$cmd .= " 2>&1";;\
\
#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\
if (!defined &sendMailfunc1) {\
*sendMailfunc1 = sub ($) {\
my ($param) = @_;;\
my $result;;\
$result = qx/$param/;;\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
}\
}\
\
#Rückgabe über diese Funktion,\
#anzeigen in dem reading dieses UserReadings:\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
#funktion falls BlockingCall abgebrochen wird:\
if (!defined &sendMailfunc3) {\
*sendMailfunc3 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20, "sendMailfunc3", "Error: timeout");;\
\
return "started cURL command...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Hallo,
nach einem fatalen Crash bin ich dabei, meine umfangreich gewachsene, aber über 10 Jahre alte fhem-Installation komplett neu aufzubauen. Unter anderem möchte ich vom alten debian "SendMail" auf die "curl"-Variante umsteigen, wie sie hier diskutiert und entwickelt wird/wurde.
Orientiert habe ich mich dabei an der Beschreibung im fhem wiki und diesem Thread.
Leider bekomme ich beim Versenden einer Nachricht die folgende Fehlermeldung im Status des "sendMail-dummys" angezeigt:
Error evaluating sendMail userReading state: Experimental aliasing via reference not enabled at (eval 393989) line 1.
Damit kann ich leider nichts anfangen und bin entsprechend aufgeschmissen. Im Web habe ich nur Hinweise gefunden, dass es möglicherweise mit der Verwendung von Pearl-Befehlen im Code zusammenhängen könnte.
Wie beschrieben, habe ich den gesamten Raspberry V komplett neu aufgesetzt. Es sollten von jeder Komponente die aktuellste Version installiert sein.
Hat Jemand einen Ansatz zur Problembeseitigung?
Vielen Dank
Ich gehe davon aus, dass es ein Backslash Problem ist!
Du hast wahrscheinlich beim Copy/Paste irgendwo einen Backslash zuviel in deinem Code.
Poste mal das list von sendMail
Hallo Torxgewinde,
manche Email-Programme stören sich daran, wenn der Absender im Header nicht eingetragen ist (als Spam behandelt oder als BCC abgelegt).
Um dein sendMail noch universeller zu machen, könnte man dieses Detail wie folgt ergänzen:
$cmd .= "-H 'To: $emailTo' ";
Gruß Robert
Hallo:
Zitat von: bertl am 25 April 2025, 13:17:49Ich gehe davon aus, dass es ein Backslash Problem ist!
Du hast wahrscheinlich beim Copy/Paste irgendwo einen Backslash zuviel in deinem Code.
Poste mal das list von sendMail
Anbei der eingefügte Code:
state:message:[\s\S]* {\ my $emailTo = 'mxxx@xxx.de';\ my $emailFrom = 'mxxx@xxx.de';\ my $emailServer = 'securesmtp.t-online.de';\ my $emailDate = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));\ \ # Passwort aus getKeyValue abrufen\ my ($err, $emailPass) = getKeyValue("${name}_password");\ if ($err || !defined $emailPass) {\ return "Error retrieving password: $err";\ }\ \ my $message = ReadingsVal($name, 'message', '???');\ my $subject = "$name: FHEM Nachricht";\ \ # Betreff extrahieren und aus der Nachricht entfernen\ if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {\ $subject = $1;\ }\ \ # Empfänger extrahieren und aus der Nachricht entfernen\ if ($message =~ s/(?:To|An)=["'](.*?)["']//) {\ $emailTo = $1;\ }\ \ #Dateianhänge bestimmen\ my @attachments;\ while ($message =~ s/(?:Attachment|Anhang)=["'](.*?)["']//) {\ my $file = $1;\ return ">>$file<< not found, not sending email" unless (-e $file);\ push (@attachments, $file);\ }\ \ #der curl Befehl wird hier aufgebaut:\ my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";\ $cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";\ $cmd .= "--write-out 'Email sent with status %{http_code}\n' ";\ $cmd .= "-H 'Subject: $subject' ";\ $cmd .= "-H 'From: $emailFrom' ";\ $cmd .= "-H 'Date: $emailDate' ";\ \ #multipart/alternative für Text und HTML EMail Body\ $cmd .= "-F '=(;type=multipart/alternative' ";\ \ # Leerzeichen und <br> bzw <br \> am Anfang entfernen:\ $message =~ s/^(\s|<br(?:[ \/]+)>)+//;\ # Leerzeichen und <br> bzw <br \> am Ende entfernen:\ $message =~ s/(\s|<br(?:[ \/]+)>)+$//;\ \ #Plaintext E-Mail Inhalt an cURL:\ $cmd .= "-F \"=$message;type=text/plain; charset=UTF-8\" ";\ #ersetze \n bzw. \r\n durch ein HTML-tag <br />:\ $message =~ s/\r?\n/<br \/>/g;\ \ #HTML E-Mail Inhalt an cURL:\ $cmd .= "-F '= <body>$message</body>;type=text/html; charset=UTF-8' ";\ $cmd .= "-F '=)' ";\ \ #jetzt alle Attachments (0..N) anhängen:\ foreach my $file (@attachments) {\ $cmd .= "-F '=@".$file.";encoder=base64' ";\ }\ \ #stderr auch mit in den output sammeln:\ $cmd .= " 2>&1";\ \ #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\ if (!defined &sendMailfunc1) {\ *sendMailfunc1 = sub ($) {\ my ($param) = @_;\ my $result;\ $result = qx/$param/;\ $result = MIME::Base64::encode_base64($result);\ $result =~ s/\n//g;\ return $result;\ }\ }\ \ #Rückgabe über diese Funktion,\ #anzeigen in dem reading dieses UserReadings:\ if (!defined &sendMailfunc2) {\ *sendMailfunc2 = sub ($) {\ my ($result) = @_;\ my $hash = $defs{$name};\ $result = MIME::Base64::decode_base64($result);\ readingsSingleUpdate($hash, $reading, $result, 1);\ }\ }\ \ #funktion falls BlockingCall abgebrochen wird:\ if (!defined &sendMailfunc3) {\ *sendMailfunc3 = sub ($) {\ my ($result) = @_;\ my $hash = $defs{$name};\ readingsSingleUpdate($hash, $reading, $result, 1);\ }\ }\ \ BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20, "sendMailfunc3", "Error: timeout");\ \ return "started cURL command...";\ },\ state:password:.* {\ # Passwort speichern\ my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";\ \ #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\ #\ # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\ # Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\ readingsBulkUpdate($hash, "password", "****");\ \ return "$ret";\ }
Ich hatte die Meldung unabhängig davon, ob ich aus dem fhem wiki oder hier aus dem Thread kopiert habe.
Die Frage ist nicht von wo du es kopiert hast, sonder wohin du es eingefügt hast!
Den Code von Torxgewinde muss man unter "Raw definition" einfügen, dann klappt das auch.
Siehe: https://wiki.fhem.de/wiki/Import_von_Code_Snippets
Zitat von: bertl am 25 April 2025, 16:41:43Die Frage ist nicht von wo du es kopiert hast, sonder wohin du es eingefügt hast!
Den Code von Torxgewinde muss man unter "Raw definition" einfügen, dann klappt das auch.
Siehe: https://wiki.fhem.de/wiki/Import_von_Code_Snippets (https://wiki.fhem.de/wiki/Import_von_Code_Snippets)
SUPER, Danke
Wieder etwas gelernt!
Durch Umzug auf neue Hardware und Bookworm bin ich auch auf das Problem mit sendmail gestoßen. Der Umstieg auf curl hat noch seine Tücken. Für welche e-mail Provider ist der wiki Code getestet worden? Mit meinen FREENET Konto gibt es Probleme. SSL/TLS kann zwar eingestellt werden, allerdings muss das Passwort "normal" (Thunderbird) übertragen werden, also unverschlüsselt? Bisher habe ich noch keine passende --login-option gefunden, oder drehe ich an der falschen Stelle.
Fehlermeldung: curl: (28) Operation timed out after 19000 milliseconds with 0 bytes received Email sent with status 000
Grüße Hans-Jörg
Mit -v
* Trying 195.4.92.210:465...
* Connected to mx.freenet.de (195.4.92.210) port 465 (#0)
* Operation timed out after 19001 milliseconds with 0 bytes received
* Closing connection 0 curl: (28) Operation timed out after 19001 milliseconds with 0 bytes received Email sent with status 000
Nach einigem suchen fand ich ein Problem mit curl und freenet, welches mit der zusätzlichen Option "--sasl-ir" behoben werden konnte. Probier das doch mal aus. Wobei mir die Timeout Fehlermeldung in diesem Zusammenhang ungewöhnlich erscheint. In meinem Thunderbird ist ebenfalls Passwort "normal" definiert, und sendMail funktioniert wie hier angegeben.
Gruß
Danny
Danke Danny,
diesen Hinweis habe ich auch schon getestet. Mit -v erhalte ich diese Meldungen:
* Trying 195.4.92.210:465...
* Connected to mx.freenet.de (195.4.92.210) port 465 (#0)
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [122 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [6 bytes data]
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
{ [325 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [4641 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [392 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
} [8 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* Server certificate:
* subject: C=DE; ST=Hamburg; L=Hamburg; O=freenet.de GmbH; CN=*.freenet.de
* start date: Feb 18 08:35:54 2025 GMT
* expire date: Feb 22 23:59:59 2026 GMT
* subjectAltName: host "mx.freenet.de" matched cert's "*.freenet.de"
* issuer: C=DE; O=Deutsche Telekom Security GmbH; CN=Telekom Security ServerID OV Class 2 CA
* SSL certificate verify ok.
{ [5 bytes data]
< 220 sub4.mail.fnrz.de ESMTP Exim 4.96 Sat, 26 Apr 2025 23:44:26 +0200
} [5 bytes data]
> EHLO Home
{ [5 bytes data]
< 250-sub4.mail.fnrz.de Hello ip-046-223-247-227.um13.pools.vodafone-ip.de [xxxxxxxxx]
< 250-SIZE 209715200
< 250-8BITMIME
< 250-AUTH LOGIN PLAIN
< 250-SMTPUTF8
< 250 HELP
} [5 bytes data]
> AUTH PLAIN AGJyZXltYXllckBmdkusbmV0LmRlACoqKio=
{ [5 bytes data]
< 535 Incorrect authentication data
* Closing connection 0
} [5 bytes data]
* TLSv1.3 (OUT), TLS alert, close notify (256):
} [2 bytes data]
curl: (67) Login denied
Email sent with status 535
Gruß Hans-Jörg
Jetzt gibt es immerhin eine Verbindung zum Server mit Details.
Ich kann jetzt allerdings auch nur mit "try and error" weiterhelfen:
- Mit Port 587 probieren: smtps://$emailServer:587
- andere "--login-options" durchprobieren, wobei das in deiner Ausgabe angezeigte "250-AUTH LOGIN PLAIN / > AUTH PLAIN" schon richtig ist
- Zur Sicherheit nochmal User/Password überprüfen
Hier eine Seite wo der Ablauf der Kommunikation beschrieben wird:
https://www.samlogic.net/articles/smtp-commands-reference-auth.htm (https://www.samlogic.net/articles/smtp-commands-reference-auth.htm)
Gruß
Danny
Danke Danny, werde ich testen. Können Sonderzeichen im Passwort bei curl Probleme machen?
Gruß Hans-Jörg
Nochmals Danke an Danny.
Habe das Passwort geändert, lag wohl an den Sonderzeichen.
Gruß Hans-Jörg
Danke Leute, ich habe versucht die Punkte unverfälscht in das Wiki (https://wiki.fhem.de/wiki/E-Mail_senden#cURL_(Linux,_Windows)) zu übernehmen.
Der Monatswechsel hat Probleme erzeugt, denn wenn das Betriebssystem auf Deutsch eingestellt ist, gibt "strftime" den Monatsnamen ebenfalls auf Deutsch aus und ist damit - zumindest bei einigen Monaten - nicht mehr Emailkonform.
Abhilfe konnte ich bei mir mit einer zusätzlichen Zeilen schaffen:
state:message:[\s\S]* {
setlocale(LC_TIME, "C");
my $emailTo = 'you@example.com';
Gruß
Danny
Danke, guter Hinweis, ich habe es im Wiki übernommen.
Um nicht irgendwelche Einstellungen zu verstellen, welche in anderen Funktionen abweichend benötigt werden, würde ich dann wie folgt vorschlagen:
my $old_locale = setlocale(LC_TIME);;\
setlocale(LC_TIME, "C");;\
my $emailDate = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));;\
setlocale(LC_TIME, $old_locale);;\
Gruß Robert
Hm,
ich würde locale nicht verändern.
my @monEN = qw(January February March April May June July September October November December);
my $mailDate = "";
my @mailArray = split(" ", strftime("%a, %d %m %Y %H:%M:%S %z", localtime(time())));
$mailArray[2] = $monEN[$mailArray[2]-1];
for my $i (0 .. $#mailArray) {
$mailDate .= $mailArray[$i] . " ";
}
chop($mailDate); # letztes Leerzeichen aus Schleifendurchlauf entfernen
Geht bestimmt noch eleganter.
Grüße Jörg
Entsprechend des E-Mail Standards RFC-5322 muss das Datums-Format wie folgt sein:
... aus mit Leerzeichen getrennten Werten für den Tag (numerisch), der Monatsbezeichnung (englisches Textkürzel), der Jahreszahl (vierstellig), der Uhrzeit (im Format ,,hh:mm") sowie deren Abweichung von der koordinierten Weltzeit (UTC).
Optional sind die Angaben einer Tagesbezeichnung am Anfang des Eintrags (englisches Textkürzel und Komma) und der Sekunden in Zeitwerten (,,hh:mm:ss") ...
https://de.wikipedia.org/wiki/Header_(E-Mail)#Date:_Absendedatum_und_Uhrzeit
https://datatracker.ietf.org/doc/html/rfc5322#page-15
Wenn man locale nicht verändern sollte (laut Jörg - keine Ahnung warum) auch wenn man locale gleich wieder zurück setzt (so wie vorher von mir beschrieben), dann würde sich folgende andere Lösung anbieten:
Den optionalen Wochentag %a weg lassen und den Monatsnamen so ähnlich wie Jörg ersetzen.
my %monate = ("Jän"=>'Jan',"Feb"=>'Feb',"Mär"=>'Mar',"Apr"=>'Apr',"Mai"=>'May',"Jun"=>'Jun',"Jul"=>'Jul',"Aug"=>'Aug',"Sep"=>'Sep',"Okt"=>'Oct',"Nov"=>'Nov',"Dez"=>'Dec');
my $emailDate = strftime("%d %b %Y %H:%M:%S %z", localtime(time()));
my $monat = (split(" ",$emailDate))[1];
$emailDate =~ s/$monat/$monate{$monat}/g;
Viele Wege führen nach Rom - keine Ahnung welcher der Beste ist!
Gruß Robert
Zitat von: bertl am 05 Mai 2025, 18:11:41Wenn man locale nicht verändern sollte (laut Jörg - keine Ahnung warum) auch wenn man locale gleich wieder zurück setzt (so wie vorher von mir beschrieben), dann würde sich folgende andere Lösung anbieten:
Den optionalen Wochentag %a weg lassen und den Monatsnamen so ähnlich wie Jörg ersetzen.
Gruß Robert
In gibt Module die nonBlocking Funktionen starten, in denen localtime genutzt wird. Doof, wenn der Zuviel genau dann localtime aufruft wenn locale verändert worden ist.
Grüße Jörg
Noch ein Lösungsansatz, dazu wird allerdings ein Modul benötigt, welches in den meisten Fällen nachinstalliert werden muss.
Hat man Email::Date::Format / libemail-date-format-perl installiert kann mit folgendem Code ein konformes Date-Feld erzeugt werden:
state:message:[\s\S]* {
use Email::Date::Format qw(email_date);
my $emailTo = 'du@example.com';;\
my $emailFrom = 'fhem@example.de';;\
my $emailServer = 'mail.your-server.de';
my $emailDate = email_date();
Ist leider mit etwas Aufwand verbunden, aber dafür wird nichts mehr verbogen.
Gruß
Danny
Moin,
Danke für die Diskussion:
Ich würde sagen, @bertl hat da mit der Lösung #1 mit dem Zwischenspeichern der locale und dem Wiederherstellen unter den Optionen eine gleichzeitig saubere, einfache und wartungsarme Lösung. Alternativ #2 wäre das Enumerieren per String-Array wie von @JoWiemann genauso OK.
Die Lösung #3 die Monate in ein Hash (Sprache1 => Englisch) zu packen und zu holen ist nicht so gut, da müssten wir ja wissen von welcher Sprache aus wir starten um es übersetzen zu können. Die Monate zu enumerieren und mit dem Index zu holen #2 ist da robust unabhängig von der aktuellen locale.
Zu dem Punkt mit den vermuteten Nebenwirkungen auf bereits geforkte Prozesse: Sobald ein Prozess geforkt ist, ist er im ersten Moment eine "Kopie" vom Parentprozess, aber ab da laufen die Prozesse getrennt (inklusive locale). nonBlocking in FHEM arbeitet mit echtem fork() und nicht mit Threads oder Ähnlichem. Der Elternprozess in FHEM ist nur ein einzelner Thread und in dem Codefragment im UserReading nicht unterbrechbar, deswegen kann man dort unbesorgt die locale einmal ändern und wiederherstellen.
Ich tendiere zu der Idee #1, da sie eben keine Wechselwirkungen auf die anderen Kindprozesse hat und mit üblichen POSIX Funktionen auskommt:
my $old_locale = setlocale(LC_TIME);
setlocale(LC_TIME, "C");
my $mailDate = strftime("%a, %d %b %Y %H:%M:%S %z", localtime);
setlocale(LC_TIME, $old_locale);
Meinungen? Ideen?
Edit #1: Das Modul Email::Date::Format zu nutzen #4, wie von @moskito erwähnt, hängt ggf. einige Nutzer ab, die das Modul dann nicht installiert bekommen. Von der Kompaktheit/Übersicht ist es natürlich sehr gut. Was sich allerdings lohnt, ist zu schauen wie es dort gelöst wurde: https://metacpan.org/dist/Email-Date-Format/source/lib/Email/Date/Format.pm Die nutzen also im Grunde auch den Index und lösen das dann mit Arrays in Strings auf. Ich denke, dann mach ich das auch einfach so, nur eben "zu Fuß" und ohne Bibliothek...
Zitat von: Torxgewinde am 09 Mai 2025, 07:39:23Meinungen?
Imo ist das mit dem simplen String-Array (#2) v.a. für Einsteiger nachvollziehbarer, als (nicht als solche direkt erkennbare) POSIX-Funktionen aufzurufen - unabhängig davon, ob das nur Auswirkungen auf einen geforkten Prozess hat, oder tatsächlich kurzfristige Änderungen auf OS-Ebene zeitigt.
Die Schleife+chop könnte man ggf. durch ein "join" ersetzen.
Die Lösung von Beta-User gefällt mir am Besten!
Was dann als fertiger Codeschnipsel so aussehen könnte:
my @mailArray = split(" ",strftime("%w %d %m %Y %H:%M:%S %z",localtime(time())));
$mailArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$mailArray[0]].",";
$mailArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$mailArray[2]-1];
my $emailDate = join(" ",@mailArray);
Danke für die Rückmeldung :) !
Dasselbe dann nochmal mit etwas weniger "Folklore" zur Klammersetzung bei built-in-Funktionen und Interpolationen:
my @mailArray = split m{\s+}x, strftime("%w %d %m %Y %H:%M:%S %z",localtime( time ));
$mailArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$mailArray[0]].',';
$mailArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$mailArray[2]-1];
my $emailDate = join ' ', @mailArray;
Generell bin ich aber weiter der Auffassung, dass man solche Funktionen nicht in userReadings-Attributen direkt unterbringen sollte, und dann auch nochmal bzgl. Interpolation der eigentlichen Anweisung einen Blick auf "qq" (oder eventuell here-doc) werfen könnte...
Ok, zusammengefasst bin ich dann bei dieser Lösung, die die locale nicht verändert und sich stark an den Ideen aus der Implementation von Email::Date::Format / libemail-date-format-perl orientiert:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message:textField-long password
attr sendMail userReadings state:message:[\s\S]* {\
my $emailTo = 'du@example.com';;\
my $emailFrom = 'fhem@example.de';;\
my $emailServer = 'mail.your-server.de';;\
\
#erzeuge Datumsangabe konform zu RFC-5322:\
my @emailDateArray = split(' ', strftime("%w %d %m %Y %H:%M:%S %z", localtime));;\
$emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . ',';;\
$emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\
my $emailDate = join(' ', @emailDateArray);;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)=["'](.*?)["']//) {\
$emailTo = $1;;\
}\
\
#Dateianhänge bestimmen\
my @attachments;;\
while ($message =~ s/(?:Attachment|Anhang)=["'](.*?)["']//) {\
my $file = $1;;\
return ">>$file<< not found, not sending email" unless (-e $file);;\
push (@attachments, $file);;\
}\
\
#der curl Befehl wird hier aufgebaut:\
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}' ";;\
$cmd .= "-H 'Subject: $subject' ";;\
$cmd .= "-H 'From: $emailFrom' ";;\
$cmd .= "-H 'Date: $emailDate' ";;\
\
#multipart/alternative für Text und HTML EMail Body\
$cmd .= "-F '=(;;type=multipart/alternative' ";;\
\
# Leerzeichen und <br> bzw <br \> am Anfang entfernen:\
$message =~ s/^(\s|<br(?:[ \/]+)>)+//;;\
# Leerzeichen und <br> bzw <br \> am Ende entfernen:\
$message =~ s/(\s|<br(?:[ \/]+)>)+$//;;\
\
#Plaintext E-Mail Inhalt an cURL:\
$cmd .= "-F \"=$message;;type=text/plain;; charset=UTF-8\" ";;\
#ersetze \n bzw. \r\n durch ein HTML-tag <br />:\
$message =~ s/\r?\n/<br \/>/g;;\
\
#HTML E-Mail Inhalt an cURL:\
$cmd .= "-F '= <body>$message</body>;;type=text/html;; charset=UTF-8' ";;\
$cmd .= "-F '=)' ";;\
\
#jetzt alle Attachments (0..N) anhängen:\
foreach my $file (@attachments) {\
$cmd .= "-F '=@".$file.";;encoder=base64' ";;\
}\
\
#stderr auch mit in den output sammeln:\
$cmd .= " 2>&1";;\
\
#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\
if (!defined &sendMailfunc1) {\
*sendMailfunc1 = sub ($) {\
my ($param) = @_;;\
my $result;;\
$result = qx/$param/;;\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
}\
}\
\
#Rückgabe über diese Funktion,\
#anzeigen in dem reading dieses UserReadings:\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
#funktion falls BlockingCall abgebrochen wird:\
if (!defined &sendMailfunc3) {\
*sendMailfunc3 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20, "sendMailfunc3", "Error: timeout");;\
\
return "started cURL command...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Hallo zusammen,
nach langen Versuchen habe ich es jetzt geschaft eine E-Mail zu versenden.
Es kam immer die Statusmeldung:
curl: option --no-progress-meter: is unknown
curl: try 'curl --help' or 'curl --manual' for more information
Jetzt habe ich versuchweise beim zusammensetzen des curl-Befehls no-progress-meter entfernt.
my $cmd = "curl -m 19 --noproxy '*' --ssl-reqd smtps://$emailServer:465 ";
und damit fuktioniert es bei mir.
Kann sich das jemand erklären?
Gruß
Ulrich
@UvG: Ich vermute mal, dass die curl version alt ist. Was liefert curl --version? Die Version sollte aktueller als 7.67.0 sein, damit der Schalter da ist: https://github.com/curl/curl/issues/4422 bzw https://curl.se/ch/7.67.0.html
Die Version liefert
curl 7.64.0 (arm-unknown-linux-gnueabihf) libcurl/7.64.0 OpenSSL/1.1.1d zlib/1.2.11 libidn2/2.0.5 libpsl/0.20.2 (+libidn2/2.0.5) libssh2/1.8.0 nghttp2/1.36.0 librtmp/2.3
Release-Date: 2019-02-06
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL
Es ist also eine ältere Version.
Hat denn das herausnehmen von no-progress-meter netative Auswirkungen?
Gruß
Ulrich
Erstmal nicht, außer dass dir der Rückgabewert unleserlich erscheinen dürfte. Du könntest also die veraltete Version beibehalten (falls es zwingende Gründe gibt). Falls dir zuviel "BlaBla" angezeigt wird, könnte der ältere Schalter --silent --show-error ggf. die Rückgabewerte auf sinnvolle Einträge reduzieren. So in der Art:
my $cmd = "curl -m 19 --noproxy '*' --silent --show-error --ssl-reqd smtps://...";
Besonders sicher (im Sinne von IT Sicherheit) ist es natürlich nicht mit solch einem altem System im Internet Daten auszutauschen, aber das wirst du vermutlich wissen. Schau' mal ob du nicht doch (irgendwann) updaten magst.
ich werde versuchen das System ingesamt updaten.
Ansonsten finde ich das Projekt super und entspricht genau meinen Vorstellungen.
Gruß
Ulrich
Meine Mails werden immer als Spam abgelehnt.
235 Authentication succeeded } [5 bytes data]
> MAIL FROM:<xxxxxxx@freenet.de> SIZE=752 { [5 bytes data] < 250 OK } [5 bytes data]
> RCPT TO:<xxxxxxxx@freenet.de> { [5 bytes data] < 250 Accepted } [5 bytes data]
> DATA { [5 bytes data] < 354 Enter message, ending with "." on a line by itself } [5 bytes data] * We are completely uploaded and fine } [5 bytes data] < 550 Spam message rejected * Connection #0 to host mx.freenet.de left intact
curl: (8) Weird server reply Email sent with status 550
Gibt es hierfür eine Lösung?
Gruß Hans-Jörg
Das klingt nach einem sehr spezifischem Problem, setze ggf. deine Adresse explizit auf die "Allowlist" (=Whitelist): https://kundenservice.freenet.de/artikel/2022
Dies habe ich schon versucht und die Einstellungen für den Spamfilter habe ich auch schon geändert.
Es bleibt wahrscheinlich nur einen anderen Provider zu nehmen.
Zitat von: mabula am 19 Mai 2025, 15:41:13Dies habe ich schon versucht und die Einstellungen für den Spamfilter habe ich auch schon geändert.
Es bleibt wahrscheinlich nur einen anderen Provider zu nehmen.
vielleicht hat es mit dieser sicherheitseinstellung zu tun?
https://fritzhelp.avm.de/help/de/FRITZ-Box-Fon-WLAN-7490/1und1/021/help_solutions_emailversand_extern (https://fritzhelp.avm.de/help/de/FRITZ-Box-Fon-WLAN-7490/1und1/021/help_solutions_emailversand_extern)
Ok, vielleicht stufen die curl-Emails wirklich schnell anhand der Kennungen als Spam ein. Da könnte der Freenet-Support helfen, oder aber du guckst nach einem anderem Anbieter. Wenn man wüsste, woran der Spamfilter festmacht, dass da Spam kommt, könnte man es ggf. ändern, aber so ist das viel Versuch und Irrtum - sorry.
Also Freenet ging nur einmal mit dem Betreff: "Einbruch". Freenet muss einen tollen "KI" Filter haben, auch Antworten auf meine Mails landen manchmal im Spam.
Wenigstens im Spam Ordner, da kann man ja noch schauen, aber die cURL Mails werden nicht mal angenommen.
Habe jetzt zu Vodafone Mail gewechselt und funktioniert.
Freenet funktioniert mit der Fritzbox ohne Probleme.
Die Spam-Mailer verwenden meist cURL. Deshalb wird vermutlich der Spamfilter bei Freenet reagieren.
Für eine Tabelle mit funktionierenden Providern.
Vodafone Mail Einstellungen: --ssl-reqd --sasl-ir smtps://$emailServer:465
Gruß
Hans-Jörg
Danke, ich habe im Wiki eine Tabelle mit den Rückmeldungen ergänzt: https://wiki.fhem.de/wiki/E-Mail_senden#Providerspezifisches
Für die Wiki Tabelle - bei mir hat es mit folgenden Providern/Einstellungen funktioniert:
my $emailServer = 'mail.gmx.net'; ...smtps://$emailServer:465
my $emailServer = 'smtp.gmail.com'; ...smtps://$emailServer:465
Hallo , auch von mir eine Rückmeldung:
Ich hab das Modul heute entdeckt und gleich aktiviert-
Es funktioniert problemlos mit meinem STRATO-Email-Server.
Ich bekomme zwar nach dem Senden den Status 250 gemeldet, aber es klappt alles.
Topp, danke für die Info. Wie lautet den der Mailserver bei Strato, mit der Info kann ich es im Wiki ergänzen.
Der Statuscode 250 ist eine normale Antwort (siehe auch https://en.wikipedia.org/wiki/List_of_SMTP_server_return_codes):
Zitat250 Requested mail action okay, completed
Bitte nicht verwechseln mit einem Errorlevel des cUrl-CLI-Programms, also dem Returnwert auf der Konsole. Der sollte bei Erfolg "0" sein (https://en.wikipedia.org/wiki/Exit_status#POSIX):
Zitat...POSIX-compatible systems typically use a convention of zero for success and nonzero for error...
Na dann passt das ja prima, mit dem Status 250.
Das ist der "smtp.strato.de" Server.
Kann ich eigentlich auch "längere" Texte mit Zeilenumbruch verschicken ?
Bzw. kann ich mit einem Kürzel im Text einen Zeilenumbruch machen ?
(derzeit wird trotz neuer Zeile im Auftrag alles "am Stück" geschrieben)
Zitat von: bertl am 26 Februar 2025, 15:59:12Ich wollte eine mehrzeilige Eingabe für den Message-Text machen und habe daher wie folgt ergänzt:
attr sendMail setList message:textField-long
Dann sollte dein Problem gelöst sein!
Zitat von: Remstäler am 23 Mai 2025, 18:23:03Ich bekomme zwar nach dem Senden den Status 250 gemeldet, aber es klappt alles.
Ich habe bei mir den HTTP Statuscode ausgewertet um gleich zu sehen, ob es sich um eine Info, einen Fehler oder was auch immer handelt.
Vielleicht ist es ja von Interesse, diese Auswertung generell zu übernehmen.
$cmd .= "--write-out 'HTTP-Statuscode %{http_code}\n' ";;\
#Rückgabe über diese Funktion,\
#anzeigen in dem reading dieses UserReadings:\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
if( $result =~ /HTTP-Statuscode 0[0-9]{2}/ ) { $result = "curl-ERROR: ".$result;; }\
elsif( $result =~ /HTTP-Statuscode 1[0-9]{2}/ ) { $result = "INFO: ".$result;; }\
elsif( $result =~ /HTTP-Statuscode 2[0-9]{2}/ ) { $result = "OK: ".$result;; }\
elsif( $result =~ /HTTP-Statuscode 3[0-9]{2}/ ) { $result = "WARNING: ".$result;; }\
elsif( $result =~ /HTTP-Statuscode [4-9][0-9]{2}/ ) { $result = "ERROR: ".$result;; }\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
Hi @bertl, das ist wie der Statuscode repräsentiert wird und deswegen würde ich es eher in dem Attribut stateFormat oder devStateIcon um-mappen auf den gewünschten Wert in der Darstellung. Ist aber eine Geschmacksfrage und deswegen IMHO optional zu behandeln.
im Kern:
...
attr sendMail stateFormat { \
my $result = ReadingsVal($name, 'state', '???');;\
\
#modify result to your liking:\
if( $result =~ /Email sent with status 0[0-9]{2}/ ) { $result = "curl-ERROR: ".$result;; }\
elsif( $result =~ /Email sent with status 1[0-9]{2}/ ) { $result = "INFO: ".$result;; }\
elsif( $result =~ /Email sent with status 2[0-9]{2}/ ) { $result = "OK: ".$result;; }\
elsif( $result =~ /Email sent with status 3[0-9]{2}/ ) { $result = "WARNING: ".$result;; }\
elsif( $result =~ /Email sent with status [4-9][0-9]{2}/ ) { $result = "ERROR: ".$result;; }\
\
return $result;;\
}...
also komplett:
defmod sendMail dummy
attr sendMail readingList message password
attr sendMail setList message:textField-long password
attr sendMail stateFormat { \
my $result = ReadingsVal($name, 'state', '???');;\
\
#modify result to your liking:\
if( $result =~ /Email sent with status 0[0-9]{2}/ ) { $result = "curl-ERROR: ".$result;; }\
elsif( $result =~ /Email sent with status 1[0-9]{2}/ ) { $result = "INFO: ".$result;; }\
elsif( $result =~ /Email sent with status 2[0-9]{2}/ ) { $result = "OK: ".$result;; }\
elsif( $result =~ /Email sent with status 3[0-9]{2}/ ) { $result = "WARNING: ".$result;; }\
elsif( $result =~ /Email sent with status [4-9][0-9]{2}/ ) { $result = "ERROR: ".$result;; }\
\
return $result;;\
}
attr sendMail userReadings state:message:[\s\S]* {\
my $emailTo = 'du@exmaple.com';;\
my $emailFrom = 'fhem@example.de';;\
my $emailServer = 'mail.your-server.de';;\
\
#erzeuge Datumsangabe konform zu RFC-5322:\
my @emailDateArray = split(' ', strftime("%w %d %m %Y %H:%M:%S %z", localtime));;\
$emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . ',';;\
$emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\
my $emailDate = join(' ', @emailDateArray);;\
\
# Passwort aus getKeyValue abrufen\
my ($err, $emailPass) = getKeyValue("${name}_password");;\
if ($err || !defined $emailPass) {\
return "Error retrieving password: $err";;\
}\
\
my $message = ReadingsVal($name, 'message', '???');;\
my $subject = "$name: FHEM Nachricht";;\
\
# Betreff extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:Subject|Betreff)=["'](.*?)["']//) {\
$subject = $1;;\
}\
\
# Empfänger extrahieren und aus der Nachricht entfernen\
if ($message =~ s/(?:To|An)=["'](.*?)["']//) {\
$emailTo = $1;;\
}\
\
#Dateianhänge bestimmen\
my @attachments;;\
while ($message =~ s/(?:Attachment|Anhang)=["'](.*?)["']//) {\
my $file = $1;;\
return ">>$file<< not found, not sending email" unless (-e $file);;\
push (@attachments, $file);;\
}\
\
#der curl Befehl wird hier aufgebaut:\
my $cmd = "curl -m 19 --noproxy '*' --no-progress-meter --ssl-reqd smtps://$emailServer:465 ";;\
$cmd .= "--user '$emailFrom:$emailPass' --mail-from '$emailFrom' --mail-rcpt '$emailTo' ";;\
$cmd .= "--write-out 'Email sent with status %{http_code}' ";;\
$cmd .= "-H 'Subject: $subject' ";;\
$cmd .= "-H 'From: $emailFrom' ";;\
$cmd .= "-H 'Date: $emailDate' ";;\
\
#multipart/alternative für Text und HTML EMail Body\
$cmd .= "-F '=(;;type=multipart/alternative' ";;\
\
# Leerzeichen und <br> bzw <br \> am Anfang entfernen:\
$message =~ s/^(\s|<br(?:[ \/]+)>)+//;;\
# Leerzeichen und <br> bzw <br \> am Ende entfernen:\
$message =~ s/(\s|<br(?:[ \/]+)>)+$//;;\
\
#Plaintext E-Mail Inhalt an cURL:\
$cmd .= "-F \"=$message;;type=text/plain;; charset=UTF-8\" ";;\
#ersetze \n bzw. \r\n durch ein HTML-tag <br />:\
$message =~ s/\r?\n/<br \/>/g;;\
\
#HTML E-Mail Inhalt an cURL:\
$cmd .= "-F '= <body>$message</body>;;type=text/html;; charset=UTF-8' ";;\
$cmd .= "-F '=)' ";;\
\
#jetzt alle Attachments (0..N) anhängen:\
foreach my $file (@attachments) {\
$cmd .= "-F '=@".$file.";;encoder=base64' ";;\
}\
\
#stderr auch mit in den output sammeln:\
$cmd .= " 2>&1";;\
\
#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\
if (!defined &sendMailfunc1) {\
*sendMailfunc1 = sub ($) {\
my ($param) = @_;;\
my $result;;\
$result = qx/$param/;;\
$result = MIME::Base64::encode_base64($result);;\
$result =~ s/\n//g;;\
return $result;;\
}\
}\
\
#Rückgabe über diese Funktion,\
#anzeigen in dem reading dieses UserReadings:\
if (!defined &sendMailfunc2) {\
*sendMailfunc2 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
$result = MIME::Base64::decode_base64($result);;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
#funktion falls BlockingCall abgebrochen wird:\
if (!defined &sendMailfunc3) {\
*sendMailfunc3 = sub ($) {\
my ($result) = @_;;\
my $hash = $defs{$name};;\
readingsSingleUpdate($hash, $reading, $result, 1);;\
}\
}\
\
BlockingCall("sendMailfunc1", $cmd, "sendMailfunc2", 20, "sendMailfunc3", "Error: timeout");;\
\
return "started cURL command...";;\
},\
state:password:.* {\
# Passwort speichern\
my $ret = setKeyValue("${name}_password", ReadingsVal($name, 'password', undef)) // "password stored";;\
\
#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\
#\
# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\
# Alternativ: { setKeyValue("sendMail_password", "geheimesPasswort") }\
readingsBulkUpdate($hash, "password", "****");;\
\
return "$ret";;\
}
Wie siehst du das?
Ich habe gerade auch nochmal die Doku dazu rausgekramt: https://everything.curl.dev/usingcurl/verbose/writeout.html