Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
aneisch committed Jun 21, 2024
1 parent 9f04e8c commit 38b4aba
Show file tree
Hide file tree
Showing 35 changed files with 160 additions and 72 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Build Status](https://github.com/aneisch/home-assistant-config/actions/workflows/check-ha-release-compatibility.yml/badge.svg)](https://github.com/aneisch/home-assistant-config/actions)
[![GitHub last commit](https://img.shields.io/github/last-commit/aneisch/home-assistant-config)](https://github.com/aneisch/home-assistant-config/commits/master)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/aneisch/home-assistant-config)](https://github.com/aneisch/home-assistant-config/graphs/commit-activity)
[![HA Version](https://img.shields.io/badge/Running%20Home%20Assistant-2024.6.3%20(Latest)-brightgreen)](https://github.com/home-assistant/home-assistant/releases/latest)
[![HA Version](https://img.shields.io/badge/Running%20Home%20Assistant-2024.6.4%20(Latest)-brightgreen)](https://github.com/home-assistant/home-assistant/releases/latest)
<br><a href="https://www.buymeacoffee.com/aneisch" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-black.png" width="150px" height="35px" alt="Buy Me A Coffee" style="height: 35px !important;width: 150px !important;" ></a>

I do my best to keep [Home Assistant](https://github.com/home-assistant/home-assistant) on the [latest release](https://github.com/home-assistant/home-assistant/releases/latest). I'm heavily utilizing [AppDaemon](http://appdaemon.readthedocs.io/en/latest/) and [NodeRed](https://flows.nodered.org/node/node-red-contrib-home-assistant-websocket) for advanced/templated automations. See [Appdaemon config](https://github.com/aneisch/home-assistant-config/tree/master/extras/appdaemon) and my NodeRed screenshots below for details. Most of my setup is run as Docker containers (see [docker-compose](https://github.com/aneisch/home-assistant-config/tree/master/extras/docker-compose) for container list).
Expand Down Expand Up @@ -57,7 +57,7 @@ Home Assistant and other containers have ingress handled automatically by [Traef
Description | value
-- | --
Lines of ESPHome YAML | 2802
Lines of Home Assistant YAML | 9182
Lines of Home Assistant YAML | 9184
[Integrations](https://www.home-assistant.io/integrations/) in use | 61
Zigbee devices in [`zha`](https://www.home-assistant.io/integrations/zha/) | 26
Z-Wave devices in [`zwave_js`](https://www.home-assistant.io/integrations/zwave_js/) | 37
Expand All @@ -81,7 +81,7 @@ Entities in the [`input_boolean`](https://www.home-assistant.io/components/input
Entities in the [`input_datetime`](https://www.home-assistant.io/components/input_datetime) domain | 38
Entities in the [`input_number`](https://www.home-assistant.io/components/input_number) domain | 6
Entities in the [`input_select`](https://www.home-assistant.io/components/input_select) domain | 19
Entities in the [`input_text`](https://www.home-assistant.io/components/input_text) domain | 15
Entities in the [`input_text`](https://www.home-assistant.io/components/input_text) domain | 16
Entities in the [`light`](https://www.home-assistant.io/components/light) domain | 39
Entities in the [`lock`](https://www.home-assistant.io/components/lock) domain | 4
Entities in the [`media_player`](https://www.home-assistant.io/components/media_player) domain | 17
Expand All @@ -101,7 +101,7 @@ Entities in the [`update`](https://www.home-assistant.io/components/update) doma
Entities in the [`vacuum`](https://www.home-assistant.io/components/vacuum) domain | 1
Entities in the [`weather`](https://www.home-assistant.io/components/weather) domain | 1
Entities in the [`zone`](https://www.home-assistant.io/components/zone) domain | 6
**Total state objects** | **1236**
**Total state objects** | **1237**
## The HACS integrations/plugins that I use:

**Appdaemon**:<br>
Expand Down
15 changes: 14 additions & 1 deletion custom_components/mail_and_packages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
CONF_IMAP_TIMEOUT,
CONF_PATH,
CONF_SCAN_INTERVAL,
CONF_VERIFY_SSL,
COORDINATOR,
DEFAULT_AMAZON_DAYS,
DEFAULT_AMAZON_FWDS,
Expand Down Expand Up @@ -147,7 +148,7 @@ async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> Non
async def async_migrate_entry(hass, config_entry):
"""Migrate an old config entry."""
version = config_entry.version
new_version = 5
new_version = 6

# 1 -> 4: Migrate format
if version == 1:
Expand Down Expand Up @@ -208,6 +209,18 @@ async def async_migrate_entry(hass, config_entry):
config_entry, data=updated_config, version=new_version
)

if version == 5:
_LOGGER.debug("Migrating from version %s", version)
updated_config = config_entry.data.copy()

if CONF_VERIFY_SSL not in updated_config:
updated_config[CONF_VERIFY_SSL] = True

if updated_config != config_entry.data:
hass.config_entries.async_update_entry(
config_entry, data=updated_config, version=new_version
)

_LOGGER.debug("Migration complete to version %s", new_version)

return True
Expand Down
12 changes: 6 additions & 6 deletions custom_components/mail_and_packages/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ def update_file_path(self) -> None:

if self._type == "usps_camera":
# Update camera image for USPS informed delivery images
image = self._coordinator.data[ATTR_IMAGE_NAME]
file_path = f"{os.path.dirname(__file__)}/mail_none.gif"

if ATTR_IMAGE_PATH in self._coordinator.data.keys():
s1 = set([ATTR_IMAGE_NAME, ATTR_IMAGE_PATH])
if s1.issubset(self._coordinator.data.keys()):
image = self._coordinator.data[ATTR_IMAGE_NAME]
path = self._coordinator.data[ATTR_IMAGE_PATH]
file_path = f"{self.hass.config.path()}/{path}{image}"
else:
Expand All @@ -157,10 +157,10 @@ def update_file_path(self) -> None:

elif self._type == "amazon_camera":
# Update camera image for Amazon deliveries
image = self._coordinator.data[ATTR_AMAZON_IMAGE]
file_path = f"{os.path.dirname(__file__)}/no_deliveries.jpg"

if ATTR_IMAGE_PATH in self._coordinator.data.keys():
s1 = set([ATTR_AMAZON_IMAGE, ATTR_IMAGE_PATH])
if s1.issubset(self._coordinator.data.keys()):
image = self._coordinator.data[ATTR_AMAZON_IMAGE]
path = f"{self._coordinator.data[ATTR_IMAGE_PATH]}amazon/"
file_path = f"{self.hass.config.path()}/{path}{image}"

Expand Down
11 changes: 8 additions & 3 deletions custom_components/mail_and_packages/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
CONF_IMAP_TIMEOUT,
CONF_PATH,
CONF_SCAN_INTERVAL,
CONF_VERIFY_SSL,
DEFAULT_ALLOW_EXTERNAL,
DEFAULT_AMAZON_DAYS,
DEFAULT_AMAZON_FWDS,
Expand Down Expand Up @@ -123,9 +124,9 @@ async def _validate_user_input(user_input: dict) -> tuple:
return errors, user_input


def _get_mailboxes(host: str, port: int, user: str, pwd: str) -> list:
def _get_mailboxes(host: str, port: int, user: str, pwd: str, verify: bool) -> list:
"""Get list of mailbox folders from mail server."""
account = login(host, port, user, pwd)
account = login(host, port, user, pwd, verify)

status, folderlist = account.list()
mailboxes = []
Expand Down Expand Up @@ -163,6 +164,7 @@ def _get_default(key: str, fallback_default: Any = None) -> None:
vol.Required(CONF_PORT, default=_get_default(CONF_PORT, 993)): cv.port,
vol.Required(CONF_USERNAME, default=_get_default(CONF_USERNAME)): cv.string,
vol.Required(CONF_PASSWORD, default=_get_default(CONF_PASSWORD)): cv.string,
vol.Required(CONF_VERIFY_SSL, default=_get_default(CONF_VERIFY_SSL)): bool,
}
)

Expand All @@ -184,6 +186,7 @@ def _get_default(key: str, fallback_default: Any = None) -> None:
data[CONF_PORT],
data[CONF_USERNAME],
data[CONF_PASSWORD],
data[CONF_VERIFY_SSL],
)
),
vol.Required(
Expand Down Expand Up @@ -238,7 +241,7 @@ def _get_default(key: str, fallback_default: Any = None) -> None:
class MailAndPackagesFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for Mail and Packages."""

VERSION = 5
VERSION = 6
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

def __init__(self):
Expand All @@ -257,6 +260,7 @@ async def async_step_user(self, user_input=None):
user_input[CONF_PORT],
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_VERIFY_SSL],
)
if not valid:
self._errors["base"] = "communication"
Expand Down Expand Up @@ -372,6 +376,7 @@ async def async_step_init(self, user_input=None):
user_input[CONF_PORT],
user_input[CONF_USERNAME],
user_input[CONF_PASSWORD],
user_input[CONF_VERIFY_SSL],
)
if not valid:
self._errors["base"] = "communication"
Expand Down
4 changes: 3 additions & 1 deletion custom_components/mail_and_packages/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

DOMAIN = "mail_and_packages"
DOMAIN_DATA = f"{DOMAIN}_data"
VERSION = "0.3.27"
VERSION = "0.3.28"
ISSUE_URL = "http://github.com/moralmunky/Home-Assistant-Mail-And-Packages"
PLATFORM = "sensor"
PLATFORMS = ["binary_sensor", "camera", "sensor"]
Expand Down Expand Up @@ -54,6 +54,7 @@
CONF_GENERATE_MP4 = "generate_mp4"
CONF_AMAZON_FWDS = "amazon_fwds"
CONF_AMAZON_DAYS = "amazon_days"
CONF_VERIFY_SSL = "verify_ssl"

# Defaults
DEFAULT_CAMERA_NAME = "Mail USPS Camera"
Expand Down Expand Up @@ -604,6 +605,7 @@
],
"subject": [
"Ein Brief kommt in Kürze bei Ihnen an",
"Ein Brief ist unterwegs zu Ihnen",
],
},
"post_de_delivered": {},
Expand Down
37 changes: 30 additions & 7 deletions custom_components/mail_and_packages/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
import os
import quopri
import re
import ssl
import subprocess # nosec
import uuid
from datetime import timezone
from email.header import decode_header
from shutil import copyfile, copytree, which
from ssl import Purpose
from typing import Any, List, Optional, Type, Union

import aiohttp
Expand Down Expand Up @@ -75,6 +77,7 @@
CONF_FOLDER,
CONF_GENERATE_MP4,
CONF_PATH,
CONF_VERIFY_SSL,
DEFAULT_AMAZON_DAYS,
OVERLAY,
SENSOR_DATA,
Expand Down Expand Up @@ -107,14 +110,20 @@ async def _check_ffmpeg() -> bool:
return which("ffmpeg")


async def _test_login(host: str, port: int, user: str, pwd: str) -> bool:
async def _test_login(host: str, port: int, user: str, pwd: str, verify: bool) -> bool:
"""Test IMAP login to specified server.
Returns success boolean
"""
# Attempt to catch invalid mail server hosts
context = ssl.create_default_context()
if not verify:
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
else:
context = ssl.create_default_context(purpose=Purpose.SERVER_AUTH)
# Catch invalid mail server / host names
try:
account = imaplib.IMAP4_SSL(host, port)
account = imaplib.IMAP4_SSL(host=host, port=port, ssl_context=context)
except Exception as err:
_LOGGER.error("Error connecting into IMAP Server: %s", str(err))
return False
Expand Down Expand Up @@ -152,12 +161,13 @@ async def process_emails(hass: HomeAssistant, config: ConfigEntry) -> dict:
pwd = config.get(CONF_PASSWORD)
folder = config.get(CONF_FOLDER)
resources = config.get(CONF_RESOURCES)
verify_ssl = config.get(CONF_VERIFY_SSL)

# Create the dict container
data = {}

# Login to email server and select the folder
account = login(host, port, user, pwd)
account = login(host, port, user, pwd, verify_ssl)

# Do not process if account returns false
if not account:
Expand Down Expand Up @@ -426,15 +436,21 @@ async def fetch(


def login(
host: str, port: int, user: str, pwd: str
host: str, port: int, user: str, pwd: str, verify: bool = True
) -> Union[bool, Type[imaplib.IMAP4_SSL]]:
"""Login to IMAP server.
Returns account object
"""
context = ssl.create_default_context()
if not verify:
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
else:
context = ssl.create_default_context(purpose=Purpose.SERVER_AUTH)
# Catch invalid mail server / host names
try:
account = imaplib.IMAP4_SSL(host, port)
account = imaplib.IMAP4_SSL(host=host, port=port, ssl_context=context)

except Exception as err:
_LOGGER.error("Network error while connecting to server: %s", str(err))
Expand Down Expand Up @@ -1083,7 +1099,14 @@ def amazon_search(
if server_response == "OK" and data[0] is not None:
count += len(data[0].split())
_LOGGER.debug("Amazon delivered email(s) found: %s", count)
get_amazon_image(data[0], account, image_path, hass, amazon_image_name)
hass.async_add_executor_job(
get_amazon_image,
data[0],
account,
image_path,
hass,
amazon_image_name,
)

return count

Expand Down
2 changes: 1 addition & 1 deletion custom_components/mail_and_packages/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
"Pillow>=9.0",
"dateparser"
],
"version": "0.3.27"
"version": "0.3.28"
}
6 changes: 4 additions & 2 deletions custom_components/mail_and_packages/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"host": "Host",
"password": "Password",
"port": "Port",
"username": "Username"
"username": "Username",
"verify_ssl": "Verify SSL Cert"
},
"description": "Please enter the connection information of your mail server.",
"title": "Mail and Packages (Step 1 of 2)"
Expand Down Expand Up @@ -66,7 +67,8 @@
"host": "Host",
"password": "Password",
"port": "Port",
"username": "Username"
"username": "Username",
"verify_ssl": "Verify SSL Cert"
},
"description": "Please enter the connection information of your mail server.",
"title": "Mail and Packages (Step 1 of 2)"
Expand Down
6 changes: 4 additions & 2 deletions custom_components/mail_and_packages/translations/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"host": "Amfitrió",
"password": "Contrasenya",
"port": "Port",
"username": "Nom d'usuari"
"username": "Nom d'usuari",
"verify_ssl": "Verify SSL Cert"
},
"description": "Introduïu la informació de connexió del vostre servidor de correu.",
"title": "Correu i paquets (pas 1 de 2)"
Expand Down Expand Up @@ -67,7 +68,8 @@
"host": "Amfitrió",
"password": "Contrasenya",
"port": "Port",
"username": "Nom d'usuari"
"username": "Nom d'usuari",
"verify_ssl": "Verify SSL Cert"
},
"description": "Introduïu la informació de connexió del vostre servidor de correu.",
"title": "Correu i paquets (pas 1 de 2)"
Expand Down
6 changes: 4 additions & 2 deletions custom_components/mail_and_packages/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"host": "Host",
"password": "Passwort",
"port": "Port",
"username": "Nutzername"
"username": "Nutzername",
"verify_ssl": "Verify SSL Cert"
},
"description": "Bitte geben Sie die Verbindungsinformationen Ihres Mailservers ein.",
"title": "Briefe und Pakete (Schritt 1 von 2)"
Expand Down Expand Up @@ -67,7 +68,8 @@
"host": "Host",
"password": "Passwort",
"port": "Port",
"username": "Nutzername"
"username": "Nutzername",
"verify_ssl": "Verify SSL Cert"
},
"description": "Bitte geben Sie die Verbindungsinformationen Ihres Mailservers ein.",
"title": "Briefe und Pakete (Schritt 1 von 2)"
Expand Down
6 changes: 4 additions & 2 deletions custom_components/mail_and_packages/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"host": "Host",
"password": "Password",
"port": "Port",
"username": "Username"
"username": "Username",
"verify_ssl": "Verify SSL Cert"
},
"description": "Please enter the connection information of your mail server.",
"title": "Mail and Packages (Step 1 of 2)"
Expand Down Expand Up @@ -67,7 +68,8 @@
"host": "Host",
"password": "Password",
"port": "Port",
"username": "Username"
"username": "Username",
"verify_ssl": "Verify SSL Cert"
},
"description": "Please enter the connection information of your mail server.",
"title": "Mail and Packages (Step 1 of 2)"
Expand Down
6 changes: 4 additions & 2 deletions custom_components/mail_and_packages/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"host": "Anfitrión",
"password": "Contraseña",
"port": "Puerto",
"username": "Nombre de usuario"
"username": "Nombre de usuario",
"verify_ssl": "Verify SSL Cert"
},
"description": "Ingrese la información de conexión de su servidor de correo.",
"title": "Correo y paquetes (Paso 1 de 2)"
Expand Down Expand Up @@ -67,7 +68,8 @@
"host": "Anfitrión",
"password": "Contraseña",
"port": "Puerto",
"username": "Nombre de usuario"
"username": "Nombre de usuario",
"verify_ssl": "Verify SSL Cert"
},
"description": "Ingrese la información de conexión de su servidor de correo.",
"title": "Correo y paquetes (Paso 1 de 2)"
Expand Down
Loading

0 comments on commit 38b4aba

Please sign in to comment.