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 (https://fhem.de/commandref.html#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/AppsInstallieren 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 -e
und dann dort eine Zeile mit:
* * * * * fetchmail -d 0