Migrate twilio webhooks to the webhook component (#17715)
* Migrate twilio webhooks to the webhook component * Fix typos in twilio * Mock out twilio in the tests * Lint * Fix regression in twilio response
This commit is contained in:
parent
599390d985
commit
5024a80d61
8 changed files with 155 additions and 59 deletions
|
@ -333,7 +333,6 @@ omit =
|
||||||
homeassistant/components/tradfri.py
|
homeassistant/components/tradfri.py
|
||||||
homeassistant/components/*/tradfri.py
|
homeassistant/components/*/tradfri.py
|
||||||
|
|
||||||
homeassistant/components/twilio.py
|
|
||||||
homeassistant/components/notify/twilio_sms.py
|
homeassistant/components/notify/twilio_sms.py
|
||||||
homeassistant/components/notify/twilio_call.py
|
homeassistant/components/notify/twilio_call.py
|
||||||
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
"""
|
|
||||||
Support for Twilio.
|
|
||||||
|
|
||||||
For more details about this component, please refer to the documentation at
|
|
||||||
https://home-assistant.io/components/twilio/
|
|
||||||
"""
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
|
||||||
from homeassistant.core import callback
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
|
||||||
|
|
||||||
REQUIREMENTS = ['twilio==6.19.1']
|
|
||||||
|
|
||||||
DOMAIN = 'twilio'
|
|
||||||
|
|
||||||
API_PATH = '/api/{}'.format(DOMAIN)
|
|
||||||
|
|
||||||
CONF_ACCOUNT_SID = 'account_sid'
|
|
||||||
CONF_AUTH_TOKEN = 'auth_token'
|
|
||||||
|
|
||||||
DATA_TWILIO = DOMAIN
|
|
||||||
DEPENDENCIES = ['http']
|
|
||||||
|
|
||||||
RECEIVED_DATA = '{}_data_received'.format(DOMAIN)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
|
||||||
DOMAIN: vol.Schema({
|
|
||||||
vol.Required(CONF_ACCOUNT_SID): cv.string,
|
|
||||||
vol.Required(CONF_AUTH_TOKEN): cv.string
|
|
||||||
}),
|
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
|
||||||
"""Set up the Twilio component."""
|
|
||||||
from twilio.rest import TwilioRestClient
|
|
||||||
conf = config[DOMAIN]
|
|
||||||
hass.data[DATA_TWILIO] = TwilioRestClient(
|
|
||||||
conf.get(CONF_ACCOUNT_SID), conf.get(CONF_AUTH_TOKEN))
|
|
||||||
hass.http.register_view(TwilioReceiveDataView())
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class TwilioReceiveDataView(HomeAssistantView):
|
|
||||||
"""Handle data from Twilio inbound messages and calls."""
|
|
||||||
|
|
||||||
url = API_PATH
|
|
||||||
name = 'api:{}'.format(DOMAIN)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def post(self, request): # pylint: disable=no-self-use
|
|
||||||
"""Handle Twilio data post."""
|
|
||||||
from twilio.twiml import TwiML
|
|
||||||
hass = request.app['hass']
|
|
||||||
data = yield from request.post()
|
|
||||||
hass.bus.async_fire(RECEIVED_DATA, dict(data))
|
|
||||||
return TwiML().to_xml()
|
|
18
homeassistant/components/twilio/.translations/en.json
Normal file
18
homeassistant/components/twilio/.translations/en.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "Twilio",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Set up the Twilio Webhook",
|
||||||
|
"description": "Are you sure you want to set up Twilio?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 Twilio messages."
|
||||||
|
},
|
||||||
|
"create_entry": {
|
||||||
|
"default": "To send events to Home Assistant, you will need to setup [Webhooks with Twilio]({twilio_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."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
homeassistant/components/twilio/__init__.py
Normal file
76
homeassistant/components/twilio/__init__.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
"""
|
||||||
|
Support for Twilio.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/twilio/
|
||||||
|
"""
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.const import CONF_WEBHOOK_ID
|
||||||
|
from homeassistant.helpers import config_entry_flow
|
||||||
|
|
||||||
|
REQUIREMENTS = ['twilio==6.19.1']
|
||||||
|
DEPENDENCIES = ['webhook']
|
||||||
|
|
||||||
|
DOMAIN = 'twilio'
|
||||||
|
|
||||||
|
CONF_ACCOUNT_SID = 'account_sid'
|
||||||
|
CONF_AUTH_TOKEN = 'auth_token'
|
||||||
|
|
||||||
|
DATA_TWILIO = DOMAIN
|
||||||
|
|
||||||
|
RECEIVED_DATA = '{}_data_received'.format(DOMAIN)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(DOMAIN): vol.Schema({
|
||||||
|
vol.Required(CONF_ACCOUNT_SID): cv.string,
|
||||||
|
vol.Required(CONF_AUTH_TOKEN): cv.string,
|
||||||
|
}),
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass, config):
|
||||||
|
"""Set up the Twilio component."""
|
||||||
|
from twilio.rest import TwilioRestClient
|
||||||
|
if DOMAIN not in config:
|
||||||
|
return True
|
||||||
|
|
||||||
|
conf = config[DOMAIN]
|
||||||
|
hass.data[DATA_TWILIO] = TwilioRestClient(
|
||||||
|
conf.get(CONF_ACCOUNT_SID), conf.get(CONF_AUTH_TOKEN))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_webhook(hass, webhook_id, request):
|
||||||
|
"""Handle incoming webhook from Twilio for inbound messages and calls."""
|
||||||
|
from twilio.twiml import TwiML
|
||||||
|
|
||||||
|
data = dict(await request.post())
|
||||||
|
data['webhook_id'] = webhook_id
|
||||||
|
hass.bus.async_fire(RECEIVED_DATA, dict(data))
|
||||||
|
|
||||||
|
return TwiML().to_xml()
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
'Twilio Webhook',
|
||||||
|
{
|
||||||
|
'twilio_url':
|
||||||
|
'https://www.twilio.com/docs/glossary/what-is-a-webhook',
|
||||||
|
'docs_url': 'https://www.home-assistant.io/components/twilio/'
|
||||||
|
}
|
||||||
|
)
|
18
homeassistant/components/twilio/strings.json
Normal file
18
homeassistant/components/twilio/strings.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"title": "Twilio",
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Set up the Twilio Webhook",
|
||||||
|
"description": "Are you sure you want to set up Twilio?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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 Twilio messages."
|
||||||
|
},
|
||||||
|
"create_entry": {
|
||||||
|
"default": "To send events to Home Assistant, you will need to setup [Webhooks with Twilio]({twilio_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."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -151,6 +151,7 @@ FLOWS = [
|
||||||
'smhi',
|
'smhi',
|
||||||
'sonos',
|
'sonos',
|
||||||
'tradfri',
|
'tradfri',
|
||||||
|
'twilio',
|
||||||
'unifi',
|
'unifi',
|
||||||
'upnp',
|
'upnp',
|
||||||
'zone',
|
'zone',
|
||||||
|
|
1
tests/components/twilio/__init__.py
Normal file
1
tests/components/twilio/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""Tests for the Twilio component."""
|
41
tests/components/twilio/test_init.py
Normal file
41
tests/components/twilio/test_init.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"""Test the init file of Twilio."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant import data_entry_flow
|
||||||
|
from homeassistant.components import twilio
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from tests.common import MockDependency
|
||||||
|
|
||||||
|
|
||||||
|
@MockDependency('twilio', 'rest')
|
||||||
|
@MockDependency('twilio', 'twiml')
|
||||||
|
async def test_config_flow_registers_webhook(hass, aiohttp_client):
|
||||||
|
"""Test setting up Twilio and sending webhook."""
|
||||||
|
with patch('homeassistant.util.get_local_ip', return_value='example.com'):
|
||||||
|
result = await hass.config_entries.flow.async_init('twilio', 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']
|
||||||
|
|
||||||
|
twilio_events = []
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def handle_event(event):
|
||||||
|
"""Handle Twilio event."""
|
||||||
|
twilio_events.append(event)
|
||||||
|
|
||||||
|
hass.bus.async_listen(twilio.RECEIVED_DATA, handle_event)
|
||||||
|
|
||||||
|
client = await aiohttp_client(hass.http.app)
|
||||||
|
await client.post('/api/webhook/{}'.format(webhook_id), data={
|
||||||
|
'hello': 'twilio'
|
||||||
|
})
|
||||||
|
|
||||||
|
assert len(twilio_events) == 1
|
||||||
|
assert twilio_events[0].data['webhook_id'] == webhook_id
|
||||||
|
assert twilio_events[0].data['hello'] == 'twilio'
|
Loading…
Add table
Reference in a new issue