Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ homeassistant/components/ambient_station/* @bachya
homeassistant/components/androidtv/* @JeffLIrion
homeassistant/components/apache_kafka/* @bachya
homeassistant/components/api/* @home-assistant/core
homeassistant/components/apprise/* @caronc
homeassistant/components/aprs/* @PhilRW
homeassistant/components/arcam_fmj/* @elupus
homeassistant/components/arduino/* @fabaff
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/apprise/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""The apprise component."""
12 changes: 12 additions & 0 deletions homeassistant/components/apprise/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"domain": "apprise",
"name": "Apprise",
"documentation": "https://www.home-assistant.io/components/apprise",
"requirements": [
"apprise==0.8.1"
],
"dependencies": [],
"codeowners": [
"@caronc"
]
}
66 changes: 66 additions & 0 deletions homeassistant/components/apprise/notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Apprise platform for notify component."""
import logging

import voluptuous as vol

import apprise

import homeassistant.helpers.config_validation as cv

from homeassistant.components.notify import (
ATTR_TITLE,
ATTR_TITLE_DEFAULT,
PLATFORM_SCHEMA,
BaseNotificationService,
)

_LOGGER = logging.getLogger(__name__)

CONF_FILE = "config"
CONF_URL = "url"

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_URL): vol.All(cv.ensure_list, [str]),
vol.Optional(CONF_FILE): cv.string,
}
)


def get_service(hass, config, discovery_info=None):
"""Get the Apprise notification service."""

# Create our object
a_obj = apprise.Apprise()

if config.get(CONF_FILE):
# Sourced from a Configuration File
a_config = apprise.AppriseConfig()
if not a_config.add(config[CONF_FILE]):
_LOGGER.error("Invalid Apprise config url provided")
return None

if not a_obj.add(a_config):
_LOGGER.error("Invalid Apprise config url provided")
return None

if config.get(CONF_URL):
# Ordered list of URLs
if not a_obj.add(config[CONF_URL]):
_LOGGER.error("Invalid Apprise URL(s) supplied")
return None

return AppriseNotificationService(a_obj)


class AppriseNotificationService(BaseNotificationService):
"""Implement the notification service for Apprise."""

def __init__(self, a_obj):
"""Initialize the service."""
self.apprise = a_obj

def send_message(self, message="", **kwargs):
"""Send a message to a specified target."""
title = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
self.apprise.notify(body=message, title=title)
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ apcaccess==0.0.13
# homeassistant.components.apns
apns2==0.3.0

# homeassistant.components.apprise
apprise==0.8.1

# homeassistant.components.aprs
aprslib==0.6.46

Expand Down
3 changes: 3 additions & 0 deletions requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ androidtv==0.0.30
# homeassistant.components.apns
apns2==0.3.0

# homeassistant.components.apprise
apprise==0.8.1

# homeassistant.components.aprs
aprslib==0.6.46

Expand Down
1 change: 1 addition & 0 deletions tests/components/apprise/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for the apprise component."""
85 changes: 85 additions & 0 deletions tests/components/apprise/test_notify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""The tests for the apprise notification platform."""
from unittest.mock import patch

from homeassistant.setup import async_setup_component

BASE_COMPONENT = "notify"


async def test_apprise_config_load_fail01(hass):
"""Test apprise configuration failures 1."""

config = {
BASE_COMPONENT: {"name": "test", "platform": "apprise", "config": "/path/"}
}

with patch("apprise.AppriseConfig.add", return_value=False):
Comment thread
MartinHjelmare marked this conversation as resolved.
assert await async_setup_component(hass, BASE_COMPONENT, config)
await hass.async_block_till_done()

Comment thread
MartinHjelmare marked this conversation as resolved.

async def test_apprise_config_load_fail02(hass):
"""Test apprise configuration failures 2."""

config = {
BASE_COMPONENT: {"name": "test", "platform": "apprise", "config": "/path/"}
}

with patch("apprise.Apprise.add", return_value=False):
with patch("apprise.AppriseConfig.add", return_value=True):
assert await async_setup_component(hass, BASE_COMPONENT, config)
await hass.async_block_till_done()

Comment thread
MartinHjelmare marked this conversation as resolved.

async def test_apprise_config_load_okay(hass, tmp_path):
"""Test apprise configuration failures."""

# Test cases where our URL is invalid
d = tmp_path / "apprise-config"
d.mkdir()
f = d / "apprise"
f.write_text("mailto://user:pass@example.com/")

config = {BASE_COMPONENT: {"name": "test", "platform": "apprise", "config": str(f)}}

assert await async_setup_component(hass, BASE_COMPONENT, config)
await hass.async_block_till_done()


async def test_apprise_url_load_fail(hass):
"""Test apprise url failure."""

config = {
BASE_COMPONENT: {
"name": "test",
"platform": "apprise",
"url": "mailto://user:pass@example.com",
}
}
with patch("apprise.Apprise.add", return_value=False):
Comment thread
MartinHjelmare marked this conversation as resolved.
assert await async_setup_component(hass, BASE_COMPONENT, config)
await hass.async_block_till_done()


async def test_apprise_notification(hass):
"""Test apprise notification."""

config = {
BASE_COMPONENT: {
"name": "test",
"platform": "apprise",
"url": "mailto://user:pass@example.com",
}
}

# Our Message
data = {"title": "Test Title", "message": "Test Message"}

with patch("apprise.Apprise") as mock_apprise:
mock_apprise.notify.return_value = True
assert await async_setup_component(hass, BASE_COMPONENT, config)
await hass.async_block_till_done()

# Test the call to our underlining notify() call
await hass.services.async_call(BASE_COMPONENT, "test", data)
await hass.async_block_till_done()
Comment thread
MartinHjelmare marked this conversation as resolved.