Neuigkeiten:

Am Sonntag den 8.12.2024 kann es ab ca. 8:00 Uhr zu kurzzeitigen Einschränkungen / Ausfällen bei den Diensten des FHEM Vereines kommen.
Die Server müssen mal gewartet und dabei neu gestartet werden ;)

Hauptmenü

📧 Fetchmail, Email von IP-Cam ohne Verzögerung an NTFY/FHEM senden

Begonnen von Torxgewinde, 23 November 2024, 21:53:53

Vorheriges Thema - Nächstes Thema

Torxgewinde

Die meisten IP-Kameras können eine E-Mail versenden, wenn eine Bewegung erkannt wird. Dies kann man als universelle Schnittstelle nutzen. NTFY bietet auch einen eigenen E-Mail Server an, der ist aber von Haus aus erstmal nicht verschlüsselt und ich wollte lieber einen IMAP Server, den ich eh nutze, abfragen.

Vorteile dieser Methode:
  • Universell und flexibel: Funktioniert mit jeder IP-Kamera, die E-Mails senden kann.
  • Direktanzeige von Bildern in NTFY.
  • Integration in bestehende Hausautomationssysteme wie FHEM möglich.

Ich wollte meinen Selfhosted NTFY Server nutzen, um mir die Bilder einer IP-Kamera direkt in NTFY anzeigen zu lassen. Dies ist sehr ähnlich zu dem was Mailcheck kann, hier nur eben losgelöst von FHEM und mit dem Bild als MIME-Attachment. Soll FHEM auch auf die E-Mail reagieren, kann man sich auf das NTFY subscriben und auch Schaltaktionen ausführen. Der Message-Flow ist dann:

IP-Kamera --[e-mail]--> IMAP-Postfach --[IMAP_IDLE]--> fetchmail --[mda-Skript]--> NTFY --[websocket]--> FHEM/Apps


Installieren von fetchmail:
sudo apt install fetchmail
Datei .fetchmailrc im Home-Verzeichnis anlegen:
set daemon 60
set logfile /home/torxgewinde/fetchmail.log
poll mail.emailserver.de proto IMAP:
    user "torxgewinde@example.com" password "geheim" is "torxgewinde" here
    mda "/home/torxgewinde/onMail.py"
    ssl
    sslcertck
    fetchall
    nokeep
    idle;

Hinweis: Fetchmail ist hier so konfiguriert, dass es die Emails alle holt und löscht. Wenn man das nicht will, bitte die Doku zu fetchmail beachten!

Skript "onMail.py", dass bei jeder neuen E-Mail als "mda" ausgeführt wird:
#!/usr/bin/python3

import sys
import email
from email.header import decode_header, Header
from email.policy import default
import requests

# Read raw email from stdin
raw_email = sys.stdin.read()

# Parse email in memory
msg = email.message_from_string(raw_email, policy=default)

# Extract and decode the subject
subject_header = msg.get("Subject", "")
decoded_subject = decode_header(subject_header)
email_subject = ""
for text, encoding in decoded_subject:
    if isinstance(text, bytes):
        text = text.decode(encoding or "utf-8")
    email_subject += text

#encoded_subject = str(Header(email_subject, charset="ascii"))
encoded_subject = Header(email_subject, charset='utf-8').encode()

# Extract the plain-text body or the first alternative part
email_body = ""
if msg.is_multipart():
    for part in msg.walk():
        content_type = part.get_content_type()
        content_disposition = str(part.get("Content-Disposition", ""))
        if content_type == "text/plain" and "attachment" not in content_disposition:
            raw_payload = part.get_payload(decode=True)
            charset = part.get_content_charset() or "utf-8"  # Default to UTF-8 if charset is missing
            try:
                email_body = raw_payload.decode(charset)
            except (UnicodeDecodeError, LookupError):  # Handle invalid encoding
                email_body = raw_payload.decode("iso-8859-1")  # Fallback to ISO-8859-1
            break
else:
    raw_payload = msg.get_payload(decode=True)
    charset = msg.get_content_charset() or "utf-8"
    try:
        email_body = raw_payload.decode(charset)
    except (UnicodeDecodeError, LookupError):
        email_body = raw_payload.decode("iso-8859-1")

# Extract JPEG attachments
jpeg_content = None
jpeg_filename = None
for part in msg.iter_attachments():
    if part.get_content_type() == "image/jpeg":
        jpeg_content = part.get_payload(decode=True)  # Decoded binary data
        jpeg_filename = part.get_filename()
        break  # Stop after first JPEG

# Credentials for Ntfy
ntfy_user = "ntfyuser"   # Replace with your username
ntfy_pass = "ntfypassword"  # Replace with your password
ntfy_url = "http://localhost:8080/Torxgewinde"

# If a JPEG attachment is found, send it via a PUT request
if jpeg_content and jpeg_filename:
    try:
        response = requests.put(
            ntfy_url,
            data=jpeg_content,
            headers={"Filename": jpeg_filename, "Title": encoded_subject},
            auth=(ntfy_user, ntfy_pass)
        )
        if response.status_code == 200:
            print("Attachment sent successfully!")
        else:
            print(f"Failed to send attachment: {response.status_code} {response.text}")
    except requests.RequestException as e:
        print(f"Error sending attachment: {e}")
else:
    print("No JPEG attachment found.")

# Print the subject and body for debugging purposes
print("Email Subject:", email_subject)
print("Email Subject2:", encoded_subject)
print("Email Body:", email_body)

Ausführbar machen des Skripts mit:
chmod +x onMail.py
Dann in das Cron vom User eintragen, alternativ kann man das Skript auch anderweitig starten:
crontab -eund dann dort eine Zeile mit:
* * * * * fetchmail -d 0