Skip to content

Instantly share code, notes, and snippets.

@walthowd
Last active February 19, 2022 21:00
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save walthowd/7a0529b6b3481af5736ea2e63e27b059 to your computer and use it in GitHub Desktop.
Save walthowd/7a0529b6b3481af5736ea2e63e27b059 to your computer and use it in GitHub Desktop.
USPS Mail Notifications with Home Assistant and Node-RED - based off https://skalavala.github.io/usps/
homeassistant:
customize:
sensor.usps_mail:
friendly_name: USPS Mail
sensor.usps_packages:
friendly_name: USPS Packages
group:
USPS:
entities:
- sensor.usps_mail
- sensor.usps_packages
- camera.usps_mail_pictures
sensor:
- platform: mqtt
name: 'USPS Mail'
state_topic: '/usps/mails'
value_template: "{{ value }}"
- platform: mqtt
name: USPS Packages
state_topic: '/usps/packages'
value_template: "{{ value }}"
camera:
- platform: generic
name: USPS Mail Pictures
still_image_url: 'http://192.168.xxx.xxx:8123/local/todays_mails.gif'
shell_command:
usps_mail_check: '/usr/bin/python3 /home/homeassistant/.homeassistant/scripts/usps-nodered.py'
[{"id":"56a1dbb3.88e2a4","type":"comment","z":"20661f8a.532c4","name":"Check USPS for mail and notify","info":"","x":150,"y":420,"wires":[]},{"id":"77de95d0.9df38c","type":"api-call-service","z":"20661f8a.532c4","name":"Notify Family","server":"e2a02faf.48099","service_domain":"notify","service":"family_iphones","data":"{ \"message\": \"msg\" }","mergecontext":"","x":950,"y":540,"wires":[[]]},{"id":"666694a4.04b05c","type":"api-call-service","z":"20661f8a.532c4","name":"Logbook entry","server":"e2a02faf.48099","service_domain":"logbook","service":"log","data":"{ \"name\":\"Node-Red\",\"message\":\"Sent today's mail notification to family iphones\" }","mergecontext":"","x":960,"y":500,"wires":[[]]},{"id":"8bf2b54e.9bfec8","type":"api-call-service","z":"20661f8a.532c4","name":"Check USPS for mail","server":"e2a02faf.48099","service_domain":"shell_command","service":"usps_mail_check","data":"{ }","mergecontext":"","x":980,"y":460,"wires":[[]]},{"id":"d79af930.fc13a8","type":"function","z":"20661f8a.532c4","name":"Format message","func":"msg.payload = { data: {'message': 'Today\\'s Mail', data: { attachment: {'url': 'https://yourname.duckdns.org:/local/todays_mails.gif' } } } };\nreturn msg;","outputs":1,"noerr":0,"x":730,"y":500,"wires":[["457de064.1d725","77de95d0.9df38c","666694a4.04b05c"]]},{"id":"76a6783a.3e4358","type":"delay","z":"20661f8a.532c4","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"12","rateUnits":"hour","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":520,"y":500,"wires":[["d79af930.fc13a8"]]},{"id":"f85bfdbd.8ce55","type":"switch","z":"20661f8a.532c4","name":"Getting mail today?","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"},{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":310,"y":500,"wires":[["76a6783a.3e4358"],[]],"outputLabels":["mail today",""]},{"id":"257bd907.8646d6","type":"server-state-changed","z":"20661f8a.532c4","name":"USPS Mail Count","server":"e2a02faf.48099","entityidfilter":"sensor.usps_mail","entityidfiltertype":"exact","haltifstate":"","x":100,"y":500,"wires":[["f85bfdbd.8ce55"]]},{"id":"764b0022.fd7d8","type":"inject","z":"20661f8a.532c4","name":"Every five minutes in the morning","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"*/5 7-9 * * *","once":false,"onceDelay":"","x":180,"y":460,"wires":[["8bf2b54e.9bfec8"]]},{"id":"e2a02faf.48099","type":"server","z":"","name":"Home Assistant","url":"https://localhost:8123","pass":"XXXX"}]
#!/usr/bin/env python3
"""
Make sure you change the parameters - username, password, mailbox,
paths and options.
"""
import email
import datetime, imaplib, re, sys
import os
import time
import subprocess
import paho.mqtt.client as mosquitto
from shutil import copyfile
# MQTT Server Address and Port
MQTT_SERVER = "127.0.0.1"
MQTT_SERVER_PORT = 1883
# MQTT User name and Password
MQTT_USERNAME = "username"
MQTT_PASSWORD = "password"
MQTT_USPS_MAIL_TOPIC = "/usps/mails"
MQTT_USPS_PACKAGE_TOPIC = "/usps/packages"
SLEEP_TIME_IN_SECONDS = 300
HOST = 'imap.gmail.com'
PORT = 993
USERNAME = 'user@email.com'
PASSWORD = 'password'
folder = 'inbox'
GIF_FILE_NAME = "todays_mails.gif"
GIF_MAKER_OPTIONS = '/usr/bin/convert -delay 300 -loop 0 '
IMAGE_OUTPUT_PATH = '/home/homeassistant/.homeassistant/www/'
# Login Method
###############################################################################
def login():
account = imaplib.IMAP4_SSL(HOST, PORT)
try:
rv, data = account.login(USERNAME, PASSWORD)
print_message ("Logged into your email server successfully!")
except imaplib.IMAP4.error:
print_message ('Failed to authenticate using the given credentials. Check your username, password, host and port.')
sys.exit(1)
return account
# Select folder inside the mailbox
###############################################################################
def selectFolder(account, folder):
rv, mailboxes = account.list()
rv, data = account.select(folder)
print_message ("Selecting folder '{}'".format(folder))
# Creates GIF image based on the attachments in the inbox
###############################################################################
def get_mails(account):
today = get_formatted_date()
image_count = 0
rv, data = account.search ( None,
'(FROM "USPS" SUBJECT "Informed Delivery Daily Digest" SINCE "' +
today + '")')
if rv == 'OK':
for num in data[0].split():
rv, data = account.fetch(num, '(RFC822)')
msg = email.message_from_string(data[0][1].decode('utf-8'))
images = []
for part in msg.walk():
if part.get_content_maintype() == "multipart":
continue
if part.get('Content-Disposition') is None:
continue
filepath = IMAGE_OUTPUT_PATH + part.get_filename()
fp = open( filepath, 'wb' )
fp.write(part.get_payload(decode=True))
images.append(filepath)
image_count = image_count + 1
fp.close()
print_message ('Found {} mails and images in your email.'.format(image_count))
if image_count > 0:
all_images = ""
for image in images:
all_images = all_images + image + " "
print_message ("Creating animated GIF out of {} images.".format(image_count))
os.system( GIF_MAKER_OPTIONS + all_images +
IMAGE_OUTPUT_PATH + GIF_FILE_NAME )
print_message ("Cleaning up...")
for image in images:
os.remove(image)
if (image_count == 0):
print_message("Found '{}' mails".format(image_count))
return image_count
# Returns today in specific format
###############################################################################
def get_formatted_date():
return datetime.datetime.today().strftime('%d-%b-%Y')
# gets packages count
###############################################################################
def package_count(account):
count = 0
today = get_formatted_date()
rv, data = account.search(None,
'(FROM "auto-reply@usps.com" SUBJECT "Item Delivered" SINCE "' +
today + '")')
if rv == 'OK':
count = len(data[0].split())
print_message("Found '{}' packages".format(count))
return count
# Prints message to console
###############################################################################
def print_message(message):
print("{} USPS: {}".format(datetime.datetime.today().strftime('%d-%b-%Y %H:%m:%S%p'), message))
# OnConnect Callback
###############################################################################
def on_connect(mosq, userdata, flags, rc):
print_message("Connected with return code: {}".format(str(rc)))
# OnLog Callback
###############################################################################
def on_log(mosq, obj, level, string):
print_message(string)
# Primary logic for the component starts here
###############################################################################
# Primary logic for the component starts here
###############################################################################
try:
# create a new MQTT Client Object
mqttc = mosquitto.Mosquitto()
# Set event callbacks
mqttc.on_connect = on_connect
# Uncomment below line to enable debug/console messages
# mqttc.on_log = on_log
# Connect to MQTT using the username/password set above
mqttc.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
mqttc.connect(MQTT_SERVER, MQTT_SERVER_PORT)
print_message ("Connected to MQTT Server successfully")
except Exception as ex:
print_message ("Error connecting to MQTT.")
print_message (str(ex))
sys.exit(1)
try:
account = login()
selectFolder(account, folder)
except Exception as exx:
print_message ("Error connecting logging into email server.")
print_message (str(exx))
sys.exit(1)
# Get the mail count and drop it in the MQTT
mc = get_mails(account)
mqttc.publish(MQTT_USPS_MAIL_TOPIC, str(mc), qos=0, retain=False)
# Get the package count and drop it in the MQTT
pc = package_count(account)
mqttc.publish(MQTT_USPS_PACKAGE_TOPIC, str(pc), qos=0, retain=False)
# if there are no mails, make sure you delete the old file,
# so that the next day, you don't see yesterday's mails
# when there are no mails, copy nomail.jpg as your default file
if mc == 0:
os.remove(IMAGE_OUTPUT_PATH + GIF_FILE_NAME)
copyfile(IMAGE_OUTPUT_PATH + "nomail.gif", IMAGE_OUTPUT_PATH + GIF_FILE_NAME)
# disconnect from MQTT
mqttc.disconnect()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment