hass-core/homeassistant/components/notify/rest.py
Isabella Gross Alström 2dcd9d94c8 Allow all success status codes in REST notify response (#22011)
For example Discord webhooks returns a 204 success code as response, which gets logged as an error in the log, even though it is successful.
Update the allowed statuses to accept all 2xx responses as successful.
2019-03-13 13:00:08 -07:00

119 lines
4.7 KiB
Python

"""
RESTful platform for notify component.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/notify.rest/
"""
import logging
import requests
import voluptuous as vol
from homeassistant.components.notify import (
ATTR_TARGET, ATTR_TITLE, ATTR_TITLE_DEFAULT, BaseNotificationService,
PLATFORM_SCHEMA)
from homeassistant.const import (CONF_RESOURCE, CONF_METHOD, CONF_NAME,
CONF_HEADERS)
import homeassistant.helpers.config_validation as cv
CONF_DATA = 'data'
CONF_DATA_TEMPLATE = 'data_template'
CONF_MESSAGE_PARAMETER_NAME = 'message_param_name'
CONF_TARGET_PARAMETER_NAME = 'target_param_name'
CONF_TITLE_PARAMETER_NAME = 'title_param_name'
DEFAULT_MESSAGE_PARAM_NAME = 'message'
DEFAULT_METHOD = 'GET'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_RESOURCE): cv.url,
vol.Optional(CONF_MESSAGE_PARAMETER_NAME,
default=DEFAULT_MESSAGE_PARAM_NAME): cv.string,
vol.Optional(CONF_METHOD, default=DEFAULT_METHOD):
vol.In(['POST', 'GET', 'POST_JSON']),
vol.Optional(CONF_HEADERS): vol.Schema({cv.string: cv.string}),
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_TARGET_PARAMETER_NAME): cv.string,
vol.Optional(CONF_TITLE_PARAMETER_NAME): cv.string,
vol.Optional(CONF_DATA): dict,
vol.Optional(CONF_DATA_TEMPLATE): {cv.match_all: cv.template_complex}
})
_LOGGER = logging.getLogger(__name__)
def get_service(hass, config, discovery_info=None):
"""Get the RESTful notification service."""
resource = config.get(CONF_RESOURCE)
method = config.get(CONF_METHOD)
headers = config.get(CONF_HEADERS)
message_param_name = config.get(CONF_MESSAGE_PARAMETER_NAME)
title_param_name = config.get(CONF_TITLE_PARAMETER_NAME)
target_param_name = config.get(CONF_TARGET_PARAMETER_NAME)
data = config.get(CONF_DATA)
data_template = config.get(CONF_DATA_TEMPLATE)
return RestNotificationService(
hass, resource, method, headers, message_param_name,
title_param_name, target_param_name, data, data_template)
class RestNotificationService(BaseNotificationService):
"""Implementation of a notification service for REST."""
def __init__(self, hass, resource, method, headers, message_param_name,
title_param_name, target_param_name, data, data_template):
"""Initialize the service."""
self._resource = resource
self._hass = hass
self._method = method.upper()
self._headers = headers
self._message_param_name = message_param_name
self._title_param_name = title_param_name
self._target_param_name = target_param_name
self._data = data
self._data_template = data_template
def send_message(self, message="", **kwargs):
"""Send a message to a user."""
data = {
self._message_param_name: message
}
if self._title_param_name is not None:
data[self._title_param_name] = kwargs.get(
ATTR_TITLE, ATTR_TITLE_DEFAULT)
if self._target_param_name is not None and ATTR_TARGET in kwargs:
# Target is a list as of 0.29 and we don't want to break existing
# integrations, so just return the first target in the list.
data[self._target_param_name] = kwargs[ATTR_TARGET][0]
if self._data:
data.update(self._data)
elif self._data_template:
def _data_template_creator(value):
"""Recursive template creator helper function."""
if isinstance(value, list):
return [_data_template_creator(item) for item in value]
if isinstance(value, dict):
return {key: _data_template_creator(item)
for key, item in value.items()}
value.hass = self._hass
return value.async_render(kwargs)
data.update(_data_template_creator(self._data_template))
if self._method == 'POST':
response = requests.post(self._resource, headers=self._headers,
data=data, timeout=10)
elif self._method == 'POST_JSON':
response = requests.post(self._resource, headers=self._headers,
json=data, timeout=10)
else: # default GET
response = requests.get(self._resource, headers=self._headers,
params=data, timeout=10)
success_codes = (200, 201, 202, 203, 204, 205, 206, 207, 208, 226)
if response.status_code not in success_codes:
_LOGGER.exception(
"Error sending message. Response %d: %s:",
response.status_code, response.reason)