mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
Migrate Mailgun to use the webhook component (#17464)
* Switch mailgun to use webhook api * Generalize webhook_config_entry_flow * Add tests for webhook_config_entry_flow * Add tests for mailgun * Remove old mailgun file from .coveragerc * Refactor WebhookFlowHandler into config_entry_flow * Remove test of helper func from IFTTT * Lint
This commit is contained in:
parent
277a9a3995
commit
d5a5695411
@ -209,7 +209,6 @@ omit =
|
|||||||
homeassistant/components/lutron_caseta.py
|
homeassistant/components/lutron_caseta.py
|
||||||
homeassistant/components/*/lutron_caseta.py
|
homeassistant/components/*/lutron_caseta.py
|
||||||
|
|
||||||
homeassistant/components/mailgun.py
|
|
||||||
homeassistant/components/*/mailgun.py
|
homeassistant/components/*/mailgun.py
|
||||||
|
|
||||||
homeassistant/components/matrix.py
|
homeassistant/components/matrix.py
|
||||||
|
@ -4,18 +4,15 @@ Support to trigger Maker IFTTT recipes.
|
|||||||
For more details about this component, please refer to the documentation at
|
For more details about this component, please refer to the documentation at
|
||||||
https://home-assistant.io/components/ifttt/
|
https://home-assistant.io/components/ifttt/
|
||||||
"""
|
"""
|
||||||
from ipaddress import ip_address
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.const import CONF_WEBHOOK_ID
|
from homeassistant.const import CONF_WEBHOOK_ID
|
||||||
from homeassistant.util.network import is_local
|
from homeassistant.helpers import config_entry_flow
|
||||||
|
|
||||||
REQUIREMENTS = ['pyfttt==0.3']
|
REQUIREMENTS = ['pyfttt==0.3']
|
||||||
DEPENDENCIES = ['webhook']
|
DEPENDENCIES = ['webhook']
|
||||||
@ -100,43 +97,11 @@ async def async_unload_entry(hass, entry):
|
|||||||
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
|
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
config_entry_flow.register_webhook_flow(
|
||||||
@config_entries.HANDLERS.register(DOMAIN)
|
DOMAIN,
|
||||||
class ConfigFlow(config_entries.ConfigFlow):
|
'IFTTT Webhook',
|
||||||
"""Handle an IFTTT config flow."""
|
{
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
|
||||||
"""Handle a user initiated set up flow."""
|
|
||||||
if self._async_current_entries():
|
|
||||||
return self.async_abort(reason='one_instance_allowed')
|
|
||||||
|
|
||||||
try:
|
|
||||||
url_parts = urlparse(self.hass.config.api.base_url)
|
|
||||||
|
|
||||||
if is_local(ip_address(url_parts.hostname)):
|
|
||||||
return self.async_abort(reason='not_internet_accessible')
|
|
||||||
except ValueError:
|
|
||||||
# If it's not an IP address, it's very likely publicly accessible
|
|
||||||
pass
|
|
||||||
|
|
||||||
if user_input is None:
|
|
||||||
return self.async_show_form(
|
|
||||||
step_id='user',
|
|
||||||
)
|
|
||||||
|
|
||||||
webhook_id = self.hass.components.webhook.async_generate_id()
|
|
||||||
webhook_url = \
|
|
||||||
self.hass.components.webhook.async_generate_url(webhook_id)
|
|
||||||
|
|
||||||
return self.async_create_entry(
|
|
||||||
title='IFTTT Webhook',
|
|
||||||
data={
|
|
||||||
CONF_WEBHOOK_ID: webhook_id
|
|
||||||
},
|
|
||||||
description_placeholders={
|
|
||||||
'applet_url': 'https://ifttt.com/maker_webhooks',
|
'applet_url': 'https://ifttt.com/maker_webhooks',
|
||||||
'webhook_url': webhook_url,
|
'docs_url': 'https://www.home-assistant.io/components/ifttt/'
|
||||||
'docs_url':
|
|
||||||
'https://www.home-assistant.io/components/ifttt/'
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
"""
|
|
||||||
Support for Mailgun.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
|
||||||
https://home-assistant.io/components/mailgun/
|
|
||||||
"""
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.const import CONF_API_KEY, CONF_DOMAIN
|
|
||||||
from homeassistant.core import callback
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = 'mailgun'
|
|
||||||
API_PATH = '/api/{}'.format(DOMAIN)
|
|
||||||
DATA_MAILGUN = DOMAIN
|
|
||||||
DEPENDENCIES = ['http']
|
|
||||||
MESSAGE_RECEIVED = '{}_message_received'.format(DOMAIN)
|
|
||||||
CONF_SANDBOX = 'sandbox'
|
|
||||||
DEFAULT_SANDBOX = False
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
|
||||||
DOMAIN: vol.Schema({
|
|
||||||
vol.Required(CONF_API_KEY): cv.string,
|
|
||||||
vol.Required(CONF_DOMAIN): cv.string,
|
|
||||||
vol.Optional(CONF_SANDBOX, default=DEFAULT_SANDBOX): cv.boolean
|
|
||||||
}),
|
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
|
||||||
"""Set up the Mailgun component."""
|
|
||||||
hass.data[DATA_MAILGUN] = config[DOMAIN]
|
|
||||||
hass.http.register_view(MailgunReceiveMessageView())
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class MailgunReceiveMessageView(HomeAssistantView):
|
|
||||||
"""Handle data from Mailgun inbound messages."""
|
|
||||||
|
|
||||||
url = API_PATH
|
|
||||||
name = 'api:{}'.format(DOMAIN)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def post(self, request): # pylint: disable=no-self-use
|
|
||||||
"""Handle Mailgun message POST."""
|
|
||||||
hass = request.app['hass']
|
|
||||||
data = yield from request.post()
|
|
||||||
hass.bus.async_fire(MESSAGE_RECEIVED, dict(data))
|
|
18
homeassistant/components/mailgun/.translations/en.json
Normal file
18
homeassistant/components/mailgun/.translations/en.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "Mailgun",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Set up the Mailgun Webhook",
|
||||||
|
"description": "Are you sure you want to set up Mailgun?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"one_instance_allowed": "Only a single instance is necessary.",
|
||||||
|
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive Mailgun messages."
|
||||||
|
},
|
||||||
|
"create_entry": {
|
||||||
|
"default": "To send events to Home Assistant, you will need to setup [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/x-www-form-urlencoded\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
homeassistant/components/mailgun/__init__.py
Normal file
67
homeassistant/components/mailgun/__init__.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
"""
|
||||||
|
Support for Mailgun.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/mailgun/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.const import CONF_API_KEY, CONF_DOMAIN, CONF_WEBHOOK_ID
|
||||||
|
from homeassistant.helpers import config_entry_flow
|
||||||
|
|
||||||
|
DOMAIN = 'mailgun'
|
||||||
|
API_PATH = '/api/{}'.format(DOMAIN)
|
||||||
|
DEPENDENCIES = ['webhook']
|
||||||
|
MESSAGE_RECEIVED = '{}_message_received'.format(DOMAIN)
|
||||||
|
CONF_SANDBOX = 'sandbox'
|
||||||
|
DEFAULT_SANDBOX = False
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(DOMAIN): vol.Schema({
|
||||||
|
vol.Required(CONF_API_KEY): cv.string,
|
||||||
|
vol.Required(CONF_DOMAIN): cv.string,
|
||||||
|
vol.Optional(CONF_SANDBOX, default=DEFAULT_SANDBOX): cv.boolean,
|
||||||
|
vol.Optional(CONF_WEBHOOK_ID): cv.string,
|
||||||
|
}),
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass, config):
|
||||||
|
"""Set up the Mailgun component."""
|
||||||
|
if DOMAIN not in config:
|
||||||
|
return True
|
||||||
|
|
||||||
|
hass.data[DOMAIN] = config[DOMAIN]
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_webhook(hass, webhook_id, request):
|
||||||
|
"""Handle incoming webhook with Mailgun inbound messages."""
|
||||||
|
data = dict(await request.post())
|
||||||
|
data['webhook_id'] = webhook_id
|
||||||
|
hass.bus.async_fire(MESSAGE_RECEIVED, data)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass, entry):
|
||||||
|
"""Configure based on config entry."""
|
||||||
|
hass.components.webhook.async_register(
|
||||||
|
entry.data[CONF_WEBHOOK_ID], handle_webhook)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass, entry):
|
||||||
|
"""Unload a config entry."""
|
||||||
|
hass.components.webhook.async_unregister(entry.data[CONF_WEBHOOK_ID])
|
||||||
|
return True
|
||||||
|
|
||||||
|
config_entry_flow.register_webhook_flow(
|
||||||
|
DOMAIN,
|
||||||
|
'Mailgun Webhook',
|
||||||
|
{
|
||||||
|
'mailgun_url':
|
||||||
|
'https://www.mailgun.com/blog/a-guide-to-using-mailguns-webhooks',
|
||||||
|
'docs_url': 'https://www.home-assistant.io/components/mailgun/'
|
||||||
|
}
|
||||||
|
)
|
18
homeassistant/components/mailgun/strings.json
Normal file
18
homeassistant/components/mailgun/strings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "Mailgun",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Set up the Mailgun Webhook",
|
||||||
|
"description": "Are you sure you want to set up Mailgun?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"one_instance_allowed": "Only a single instance is necessary.",
|
||||||
|
"not_internet_accessible": "Your Home Assistant instance needs to be accessible from the internet to receive Mailgun messages."
|
||||||
|
},
|
||||||
|
"create_entry": {
|
||||||
|
"default": "To send events to Home Assistant, you will need to setup [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/x-www-form-urlencoded\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,8 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.mailgun import CONF_SANDBOX, DATA_MAILGUN
|
from homeassistant.components.mailgun import (
|
||||||
|
CONF_SANDBOX, DOMAIN as MAILGUN_DOMAIN)
|
||||||
from homeassistant.components.notify import (
|
from homeassistant.components.notify import (
|
||||||
PLATFORM_SCHEMA, BaseNotificationService, ATTR_TITLE, ATTR_TITLE_DEFAULT,
|
PLATFORM_SCHEMA, BaseNotificationService, ATTR_TITLE, ATTR_TITLE_DEFAULT,
|
||||||
ATTR_DATA)
|
ATTR_DATA)
|
||||||
@ -35,7 +36,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||||||
|
|
||||||
def get_service(hass, config, discovery_info=None):
|
def get_service(hass, config, discovery_info=None):
|
||||||
"""Get the Mailgun notification service."""
|
"""Get the Mailgun notification service."""
|
||||||
data = hass.data[DATA_MAILGUN]
|
data = hass.data[MAILGUN_DOMAIN]
|
||||||
mailgun_service = MailgunNotificationService(
|
mailgun_service = MailgunNotificationService(
|
||||||
data.get(CONF_DOMAIN), data.get(CONF_SANDBOX),
|
data.get(CONF_DOMAIN), data.get(CONF_SANDBOX),
|
||||||
data.get(CONF_API_KEY), config.get(CONF_SENDER),
|
data.get(CONF_API_KEY), config.get(CONF_SENDER),
|
||||||
|
@ -143,6 +143,7 @@ FLOWS = [
|
|||||||
'ifttt',
|
'ifttt',
|
||||||
'ios',
|
'ios',
|
||||||
'lifx',
|
'lifx',
|
||||||
|
'mailgun',
|
||||||
'mqtt',
|
'mqtt',
|
||||||
'nest',
|
'nest',
|
||||||
'openuv',
|
'openuv',
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
"""Helpers for data entry flows for config entries."""
|
"""Helpers for data entry flows for config entries."""
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from ipaddress import ip_address
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.util.network import is_local
|
||||||
|
|
||||||
|
|
||||||
def register_discovery_flow(domain, title, discovery_function,
|
def register_discovery_flow(domain, title, discovery_function,
|
||||||
@ -12,6 +15,14 @@ def register_discovery_flow(domain, title, discovery_function,
|
|||||||
connection_class))
|
connection_class))
|
||||||
|
|
||||||
|
|
||||||
|
def register_webhook_flow(domain, title, description_placeholder,
|
||||||
|
allow_multiple=False):
|
||||||
|
"""Register flow for webhook integrations."""
|
||||||
|
config_entries.HANDLERS.register(domain)(
|
||||||
|
partial(WebhookFlowHandler, domain, title, description_placeholder,
|
||||||
|
allow_multiple))
|
||||||
|
|
||||||
|
|
||||||
class DiscoveryFlowHandler(config_entries.ConfigFlow):
|
class DiscoveryFlowHandler(config_entries.ConfigFlow):
|
||||||
"""Handle a discovery config flow."""
|
"""Handle a discovery config flow."""
|
||||||
|
|
||||||
@ -84,3 +95,50 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow):
|
|||||||
title=self._title,
|
title=self._title,
|
||||||
data={},
|
data={},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WebhookFlowHandler(config_entries.ConfigFlow):
|
||||||
|
"""Handle a webhook config flow."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
def __init__(self, domain, title, description_placeholder,
|
||||||
|
allow_multiple):
|
||||||
|
"""Initialize the discovery config flow."""
|
||||||
|
self._domain = domain
|
||||||
|
self._title = title
|
||||||
|
self._description_placeholder = description_placeholder
|
||||||
|
self._allow_multiple = allow_multiple
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Handle a user initiated set up flow to create a webhook."""
|
||||||
|
if not self._allow_multiple and self._async_current_entries():
|
||||||
|
return self.async_abort(reason='one_instance_allowed')
|
||||||
|
|
||||||
|
try:
|
||||||
|
url_parts = urlparse(self.hass.config.api.base_url)
|
||||||
|
|
||||||
|
if is_local(ip_address(url_parts.hostname)):
|
||||||
|
return self.async_abort(reason='not_internet_accessible')
|
||||||
|
except ValueError:
|
||||||
|
# If it's not an IP address, it's very likely publicly accessible
|
||||||
|
pass
|
||||||
|
|
||||||
|
if user_input is None:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id='user',
|
||||||
|
)
|
||||||
|
|
||||||
|
webhook_id = self.hass.components.webhook.async_generate_id()
|
||||||
|
webhook_url = \
|
||||||
|
self.hass.components.webhook.async_generate_url(webhook_id)
|
||||||
|
|
||||||
|
self._description_placeholder['webhook_url'] = webhook_url
|
||||||
|
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=self._title,
|
||||||
|
data={
|
||||||
|
'webhook_id': webhook_id
|
||||||
|
},
|
||||||
|
description_placeholders=self._description_placeholder
|
||||||
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""Test the init file of IFTTT."""
|
"""Test the init file of IFTTT."""
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant import data_entry_flow
|
from homeassistant import data_entry_flow
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
@ -36,13 +36,3 @@ async def test_config_flow_registers_webhook(hass, aiohttp_client):
|
|||||||
assert len(ifttt_events) == 1
|
assert len(ifttt_events) == 1
|
||||||
assert ifttt_events[0].data['webhook_id'] == webhook_id
|
assert ifttt_events[0].data['webhook_id'] == webhook_id
|
||||||
assert ifttt_events[0].data['hello'] == 'ifttt'
|
assert ifttt_events[0].data['hello'] == 'ifttt'
|
||||||
|
|
||||||
|
|
||||||
async def test_config_flow_aborts_external_url(hass, aiohttp_client):
|
|
||||||
"""Test setting up IFTTT and sending webhook."""
|
|
||||||
hass.config.api = Mock(base_url='http://192.168.1.10')
|
|
||||||
result = await hass.config_entries.flow.async_init('ifttt', context={
|
|
||||||
'source': 'user'
|
|
||||||
})
|
|
||||||
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
|
||||||
assert result['reason'] == 'not_internet_accessible'
|
|
||||||
|
1
tests/components/mailgun/__init__.py
Normal file
1
tests/components/mailgun/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for the Mailgun component."""
|
39
tests/components/mailgun/test_init.py
Normal file
39
tests/components/mailgun/test_init.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""Test the init file of Mailgun."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant import data_entry_flow
|
||||||
|
from homeassistant.components import mailgun
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
|
||||||
|
async def test_config_flow_registers_webhook(hass, aiohttp_client):
|
||||||
|
"""Test setting up Mailgun and sending webhook."""
|
||||||
|
with patch('homeassistant.util.get_local_ip', return_value='example.com'):
|
||||||
|
result = await hass.config_entries.flow.async_init('mailgun', context={
|
||||||
|
'source': 'user'
|
||||||
|
})
|
||||||
|
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM, result
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result['flow_id'], {})
|
||||||
|
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
webhook_id = result['result'].data['webhook_id']
|
||||||
|
|
||||||
|
mailgun_events = []
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def handle_event(event):
|
||||||
|
"""Handle Mailgun event."""
|
||||||
|
mailgun_events.append(event)
|
||||||
|
|
||||||
|
hass.bus.async_listen(mailgun.MESSAGE_RECEIVED, handle_event)
|
||||||
|
|
||||||
|
client = await aiohttp_client(hass.http.app)
|
||||||
|
await client.post('/api/webhook/{}'.format(webhook_id), data={
|
||||||
|
'hello': 'mailgun'
|
||||||
|
})
|
||||||
|
|
||||||
|
assert len(mailgun_events) == 1
|
||||||
|
assert mailgun_events[0].data['webhook_id'] == webhook_id
|
||||||
|
assert mailgun_events[0].data['hello'] == 'mailgun'
|
@ -1,5 +1,5 @@
|
|||||||
"""Tests for the Config Entry Flow helper."""
|
"""Tests for the Config Entry Flow helper."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch, Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ from tests.common import MockConfigEntry, MockModule
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def flow_conf(hass):
|
def discovery_flow_conf(hass):
|
||||||
"""Register a handler."""
|
"""Register a handler."""
|
||||||
handler_conf = {
|
handler_conf = {
|
||||||
'discovered': False,
|
'discovered': False,
|
||||||
@ -26,7 +26,18 @@ def flow_conf(hass):
|
|||||||
yield handler_conf
|
yield handler_conf
|
||||||
|
|
||||||
|
|
||||||
async def test_single_entry_allowed(hass, flow_conf):
|
@pytest.fixture
|
||||||
|
def webhook_flow_conf(hass):
|
||||||
|
"""Register a handler."""
|
||||||
|
with patch.dict(config_entries.HANDLERS):
|
||||||
|
config_entry_flow.register_webhook_flow(
|
||||||
|
'test_single', 'Test Single', {}, False)
|
||||||
|
config_entry_flow.register_webhook_flow(
|
||||||
|
'test_multiple', 'Test Multiple', {}, True)
|
||||||
|
yield {}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_single_entry_allowed(hass, discovery_flow_conf):
|
||||||
"""Test only a single entry is allowed."""
|
"""Test only a single entry is allowed."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
@ -38,7 +49,7 @@ async def test_single_entry_allowed(hass, flow_conf):
|
|||||||
assert result['reason'] == 'single_instance_allowed'
|
assert result['reason'] == 'single_instance_allowed'
|
||||||
|
|
||||||
|
|
||||||
async def test_user_no_devices_found(hass, flow_conf):
|
async def test_user_no_devices_found(hass, discovery_flow_conf):
|
||||||
"""Test if no devices found."""
|
"""Test if no devices found."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
@ -51,18 +62,18 @@ async def test_user_no_devices_found(hass, flow_conf):
|
|||||||
assert result['reason'] == 'no_devices_found'
|
assert result['reason'] == 'no_devices_found'
|
||||||
|
|
||||||
|
|
||||||
async def test_user_has_confirmation(hass, flow_conf):
|
async def test_user_has_confirmation(hass, discovery_flow_conf):
|
||||||
"""Test user requires no confirmation to setup."""
|
"""Test user requires no confirmation to setup."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
flow_conf['discovered'] = True
|
discovery_flow_conf['discovered'] = True
|
||||||
|
|
||||||
result = await flow.async_step_user()
|
result = await flow.async_step_user()
|
||||||
|
|
||||||
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
|
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_single_instance(hass, flow_conf):
|
async def test_discovery_single_instance(hass, discovery_flow_conf):
|
||||||
"""Test we ask for confirmation via discovery."""
|
"""Test we ask for confirmation via discovery."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
@ -74,7 +85,7 @@ async def test_discovery_single_instance(hass, flow_conf):
|
|||||||
assert result['reason'] == 'single_instance_allowed'
|
assert result['reason'] == 'single_instance_allowed'
|
||||||
|
|
||||||
|
|
||||||
async def test_discovery_confirmation(hass, flow_conf):
|
async def test_discovery_confirmation(hass, discovery_flow_conf):
|
||||||
"""Test we ask for confirmation via discovery."""
|
"""Test we ask for confirmation via discovery."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
@ -88,7 +99,7 @@ async def test_discovery_confirmation(hass, flow_conf):
|
|||||||
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
|
||||||
|
|
||||||
async def test_multiple_discoveries(hass, flow_conf):
|
async def test_multiple_discoveries(hass, discovery_flow_conf):
|
||||||
"""Test we only create one instance for multiple discoveries."""
|
"""Test we only create one instance for multiple discoveries."""
|
||||||
loader.set_component(hass, 'test', MockModule('test'))
|
loader.set_component(hass, 'test', MockModule('test'))
|
||||||
|
|
||||||
@ -102,7 +113,7 @@ async def test_multiple_discoveries(hass, flow_conf):
|
|||||||
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
|
||||||
|
|
||||||
async def test_only_one_in_progress(hass, flow_conf):
|
async def test_only_one_in_progress(hass, discovery_flow_conf):
|
||||||
"""Test a user initialized one will finish and cancel discovered one."""
|
"""Test a user initialized one will finish and cancel discovered one."""
|
||||||
loader.set_component(hass, 'test', MockModule('test'))
|
loader.set_component(hass, 'test', MockModule('test'))
|
||||||
|
|
||||||
@ -127,22 +138,71 @@ async def test_only_one_in_progress(hass, flow_conf):
|
|||||||
assert len(hass.config_entries.flow.async_progress()) == 0
|
assert len(hass.config_entries.flow.async_progress()) == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_import_no_confirmation(hass, flow_conf):
|
async def test_import_no_confirmation(hass, discovery_flow_conf):
|
||||||
"""Test import requires no confirmation to set up."""
|
"""Test import requires no confirmation to set up."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
flow_conf['discovered'] = True
|
discovery_flow_conf['discovered'] = True
|
||||||
|
|
||||||
result = await flow.async_step_import(None)
|
result = await flow.async_step_import(None)
|
||||||
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
|
||||||
|
|
||||||
async def test_import_single_instance(hass, flow_conf):
|
async def test_import_single_instance(hass, discovery_flow_conf):
|
||||||
"""Test import doesn't create second instance."""
|
"""Test import doesn't create second instance."""
|
||||||
flow = config_entries.HANDLERS['test']()
|
flow = config_entries.HANDLERS['test']()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
flow_conf['discovered'] = True
|
discovery_flow_conf['discovered'] = True
|
||||||
MockConfigEntry(domain='test').add_to_hass(hass)
|
MockConfigEntry(domain='test').add_to_hass(hass)
|
||||||
|
|
||||||
result = await flow.async_step_import(None)
|
result = await flow.async_step_import(None)
|
||||||
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_single_entry_allowed(hass, webhook_flow_conf):
|
||||||
|
"""Test only a single entry is allowed."""
|
||||||
|
flow = config_entries.HANDLERS['test_single']()
|
||||||
|
flow.hass = hass
|
||||||
|
|
||||||
|
MockConfigEntry(domain='test_single').add_to_hass(hass)
|
||||||
|
result = await flow.async_step_user()
|
||||||
|
|
||||||
|
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result['reason'] == 'one_instance_allowed'
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_multiple_entries_allowed(hass, webhook_flow_conf):
|
||||||
|
"""Test multiple entries are allowed when specified."""
|
||||||
|
flow = config_entries.HANDLERS['test_multiple']()
|
||||||
|
flow.hass = hass
|
||||||
|
|
||||||
|
MockConfigEntry(domain='test_multiple').add_to_hass(hass)
|
||||||
|
hass.config.api = Mock(base_url='http://example.com')
|
||||||
|
|
||||||
|
result = await flow.async_step_user()
|
||||||
|
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_config_flow_aborts_external_url(hass,
|
||||||
|
webhook_flow_conf):
|
||||||
|
"""Test configuring a webhook without an external url."""
|
||||||
|
flow = config_entries.HANDLERS['test_single']()
|
||||||
|
flow.hass = hass
|
||||||
|
|
||||||
|
hass.config.api = Mock(base_url='http://192.168.1.10')
|
||||||
|
result = await flow.async_step_user()
|
||||||
|
|
||||||
|
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result['reason'] == 'not_internet_accessible'
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_config_flow_registers_webhook(hass, webhook_flow_conf):
|
||||||
|
"""Test setting up an entry creates a webhook."""
|
||||||
|
flow = config_entries.HANDLERS['test_single']()
|
||||||
|
flow.hass = hass
|
||||||
|
|
||||||
|
hass.config.api = Mock(base_url='http://example.com')
|
||||||
|
result = await flow.async_step_user(user_input={})
|
||||||
|
|
||||||
|
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result['data']['webhook_id'] is not None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user