Add Initial Mailbox panel and sensor (#8233)
* Initial implementation of Asterisk Mailbox * Rework asterisk_mbox handler to avoid using the hass.data hash. Fix requirements. * Handle potential asterisk server disconnect. bump asterisk_mbox requirement to 0.4.0 * Use async method for mp3 fetch from server * Add http as dependency * Minor log fix. try to force Travis to rebuild * Updates based on review * Fix error handling as per review * Fix error handling as per review * Refactor voicemail into mailbox component * Hide mailbox component from front page * Add demo for mailbox * Add tests for mailbox * Remove asterisk_mbox sensor and replace with a generic mailbox sensor * Fix linting errors * Remove mailbox sensor. Remove demo.mp3. Split entity from platform object. * Update mailbox test * Update mailbox test * Use events to indicate state change rather than entity last-updated * Make mailbox platform calls async. Fix other review concerns * Rewrite mailbox tests to live at root level and be async. Fixmailbox dependency on http * Only store number of messages not content in mailbox entity
This commit is contained in:
parent
5696e38dd6
commit
d74f4eaf52
9 changed files with 603 additions and 0 deletions
|
@ -29,6 +29,9 @@ omit =
|
||||||
homeassistant/components/arlo.py
|
homeassistant/components/arlo.py
|
||||||
homeassistant/components/*/arlo.py
|
homeassistant/components/*/arlo.py
|
||||||
|
|
||||||
|
homeassistant/components/asterisk_mbox.py
|
||||||
|
homeassistant/components/*/asterisk_mbox.py
|
||||||
|
|
||||||
homeassistant/components/axis.py
|
homeassistant/components/axis.py
|
||||||
homeassistant/components/*/axis.py
|
homeassistant/components/*/axis.py
|
||||||
|
|
||||||
|
|
82
homeassistant/components/asterisk_mbox.py
Normal file
82
homeassistant/components/asterisk_mbox.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
"""Support for Asterisk Voicemail interface."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers import discovery
|
||||||
|
from homeassistant.const import (CONF_HOST,
|
||||||
|
CONF_PORT, CONF_PASSWORD)
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers.dispatcher import (async_dispatcher_connect,
|
||||||
|
async_dispatcher_send)
|
||||||
|
|
||||||
|
REQUIREMENTS = ['asterisk_mbox==0.4.0']
|
||||||
|
|
||||||
|
SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated'
|
||||||
|
SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request'
|
||||||
|
|
||||||
|
DOMAIN = 'asterisk_mbox'
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.Schema({
|
||||||
|
vol.Required(CONF_HOST): cv.string,
|
||||||
|
vol.Required(CONF_PORT): int,
|
||||||
|
vol.Required(CONF_PASSWORD): cv.string,
|
||||||
|
}),
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Set up for the Asterisk Voicemail box."""
|
||||||
|
conf = config.get(DOMAIN)
|
||||||
|
|
||||||
|
host = conf.get(CONF_HOST)
|
||||||
|
port = conf.get(CONF_PORT)
|
||||||
|
password = conf.get(CONF_PASSWORD)
|
||||||
|
|
||||||
|
hass.data[DOMAIN] = AsteriskData(hass, host, port, password)
|
||||||
|
|
||||||
|
discovery.load_platform(hass, "mailbox", DOMAIN, {}, config)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AsteriskData(object):
|
||||||
|
"""Store Asterisk mailbox data."""
|
||||||
|
|
||||||
|
def __init__(self, hass, host, port, password):
|
||||||
|
"""Init the Asterisk data object."""
|
||||||
|
from asterisk_mbox import Client as asteriskClient
|
||||||
|
|
||||||
|
self.hass = hass
|
||||||
|
self.client = asteriskClient(host, port, password, self.handle_data)
|
||||||
|
self.messages = []
|
||||||
|
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass, SIGNAL_MESSAGE_REQUEST, self._request_messages)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def handle_data(self, command, msg):
|
||||||
|
"""Handle changes to the mailbox."""
|
||||||
|
from asterisk_mbox.commands import CMD_MESSAGE_LIST
|
||||||
|
|
||||||
|
if command == CMD_MESSAGE_LIST:
|
||||||
|
_LOGGER.info("AsteriskVM sent updated message list")
|
||||||
|
self.messages = sorted(msg,
|
||||||
|
key=lambda item: item['info']['origtime'],
|
||||||
|
reverse=True)
|
||||||
|
async_dispatcher_send(self.hass, SIGNAL_MESSAGE_UPDATE,
|
||||||
|
self.messages)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _request_messages(self):
|
||||||
|
"""Handle changes to the mailbox."""
|
||||||
|
_LOGGER.info("Requesting message list")
|
||||||
|
self.client.messages()
|
|
@ -31,6 +31,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
|
||||||
'sensor',
|
'sensor',
|
||||||
'switch',
|
'switch',
|
||||||
'tts',
|
'tts',
|
||||||
|
'mailbox',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
254
homeassistant/components/mailbox/__init__.py
Normal file
254
homeassistant/components/mailbox/__init__.py
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
"""
|
||||||
|
Provides functionality for mailboxes.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/mailbox/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from contextlib import suppress
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
import async_timeout
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
from aiohttp.web_exceptions import HTTPNotFound
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.helpers import config_per_platform, discovery
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.components.http import HomeAssistantView
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.setup import async_prepare_setup_platform
|
||||||
|
|
||||||
|
DEPENDENCIES = ['http']
|
||||||
|
DOMAIN = 'mailbox'
|
||||||
|
EVENT = 'mailbox_updated'
|
||||||
|
CONTENT_TYPE_MPEG = 'audio/mpeg'
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_setup(hass, config):
|
||||||
|
"""Track states and offer events for mailboxes."""
|
||||||
|
mailboxes = []
|
||||||
|
hass.components.frontend.register_built_in_panel(
|
||||||
|
'mailbox', 'Mailbox', 'mdi:account-location')
|
||||||
|
hass.http.register_view(MailboxPlatformsView(mailboxes))
|
||||||
|
hass.http.register_view(MailboxMessageView(mailboxes))
|
||||||
|
hass.http.register_view(MailboxMediaView(mailboxes))
|
||||||
|
hass.http.register_view(MailboxDeleteView(mailboxes))
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_setup_platform(p_type, p_config=None, discovery_info=None):
|
||||||
|
"""Set up a mailbox platform."""
|
||||||
|
if p_config is None:
|
||||||
|
p_config = {}
|
||||||
|
if discovery_info is None:
|
||||||
|
discovery_info = {}
|
||||||
|
|
||||||
|
platform = yield from async_prepare_setup_platform(
|
||||||
|
hass, config, DOMAIN, p_type)
|
||||||
|
|
||||||
|
if platform is None:
|
||||||
|
_LOGGER.error("Unknown mailbox platform specified")
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.info("Setting up %s.%s", DOMAIN, p_type)
|
||||||
|
mailbox = None
|
||||||
|
try:
|
||||||
|
if hasattr(platform, 'async_get_handler'):
|
||||||
|
mailbox = yield from \
|
||||||
|
platform.async_get_handler(hass, p_config, discovery_info)
|
||||||
|
elif hasattr(platform, 'get_handler'):
|
||||||
|
mailbox = yield from hass.async_add_job(
|
||||||
|
platform.get_handler, hass, p_config, discovery_info)
|
||||||
|
else:
|
||||||
|
raise HomeAssistantError("Invalid mailbox platform.")
|
||||||
|
|
||||||
|
if mailbox is None:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Failed to initialize mailbox platform %s", p_type)
|
||||||
|
return
|
||||||
|
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
_LOGGER.exception('Error setting up platform %s', p_type)
|
||||||
|
return
|
||||||
|
|
||||||
|
mailboxes.append(mailbox)
|
||||||
|
mailbox_entity = MailboxEntity(hass, mailbox)
|
||||||
|
component = EntityComponent(
|
||||||
|
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
|
||||||
|
yield from component.async_add_entity(mailbox_entity)
|
||||||
|
|
||||||
|
setup_tasks = [async_setup_platform(p_type, p_config) for p_type, p_config
|
||||||
|
in config_per_platform(config, DOMAIN)]
|
||||||
|
|
||||||
|
if setup_tasks:
|
||||||
|
yield from asyncio.wait(setup_tasks, loop=hass.loop)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_platform_discovered(platform, info):
|
||||||
|
"""Handle for discovered platform."""
|
||||||
|
yield from async_setup_platform(platform, discovery_info=info)
|
||||||
|
|
||||||
|
discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxEntity(Entity):
|
||||||
|
"""Entity for each mailbox platform."""
|
||||||
|
|
||||||
|
def __init__(self, hass, mailbox):
|
||||||
|
"""Initialize mailbox entity."""
|
||||||
|
self.mailbox = mailbox
|
||||||
|
self.hass = hass
|
||||||
|
self.message_count = 0
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _mailbox_updated(event):
|
||||||
|
self.hass.async_add_job(self.async_update_ha_state(True))
|
||||||
|
|
||||||
|
hass.bus.async_listen(EVENT, _mailbox_updated)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state of the binary sensor."""
|
||||||
|
return str(self.message_count)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the entity."""
|
||||||
|
return self.mailbox.name
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_update(self):
|
||||||
|
"""Retrieve messages from platform."""
|
||||||
|
messages = yield from self.mailbox.async_get_messages()
|
||||||
|
self.message_count = len(messages)
|
||||||
|
|
||||||
|
|
||||||
|
class Mailbox(object):
|
||||||
|
"""Represent an mailbox device."""
|
||||||
|
|
||||||
|
def __init__(self, hass, name):
|
||||||
|
"""Initialize mailbox object."""
|
||||||
|
self.hass = hass
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def async_update(self):
|
||||||
|
"""Send event notification of updated mailbox."""
|
||||||
|
self.hass.bus.async_fire(EVENT)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def media_type(self):
|
||||||
|
"""Return the supported media type."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_media(self, msgid):
|
||||||
|
"""Return the media blob for the msgid."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_messages(self):
|
||||||
|
"""Return a list of the current messages."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def async_delete(self, msgid):
|
||||||
|
"""Delete the specified messages."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class StreamError(Exception):
|
||||||
|
"""Media streaming exception."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxView(HomeAssistantView):
|
||||||
|
"""Base mailbox view."""
|
||||||
|
|
||||||
|
def __init__(self, mailboxes):
|
||||||
|
"""Initialize a basic mailbox view."""
|
||||||
|
self.mailboxes = mailboxes
|
||||||
|
|
||||||
|
def get_mailbox(self, platform):
|
||||||
|
"""Retrieve the specified mailbox."""
|
||||||
|
for mailbox in self.mailboxes:
|
||||||
|
if mailbox.name == platform:
|
||||||
|
return mailbox
|
||||||
|
raise HTTPNotFound
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxPlatformsView(MailboxView):
|
||||||
|
"""View to return the list of mailbox platforms."""
|
||||||
|
|
||||||
|
url = "/api/mailbox/platforms"
|
||||||
|
name = "api:mailbox:platforms"
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def get(self, request):
|
||||||
|
"""Retrieve list of platforms."""
|
||||||
|
platforms = []
|
||||||
|
for mailbox in self.mailboxes:
|
||||||
|
platforms.append(mailbox.name)
|
||||||
|
return self.json(platforms)
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxMessageView(MailboxView):
|
||||||
|
"""View to return the list of messages."""
|
||||||
|
|
||||||
|
url = "/api/mailbox/messages/{platform}"
|
||||||
|
name = "api:mailbox:messages"
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def get(self, request, platform):
|
||||||
|
"""Retrieve messages."""
|
||||||
|
mailbox = self.get_mailbox(platform)
|
||||||
|
messages = yield from mailbox.async_get_messages()
|
||||||
|
return self.json(messages)
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxDeleteView(MailboxView):
|
||||||
|
"""View to delete selected messages."""
|
||||||
|
|
||||||
|
url = "/api/mailbox/delete/{platform}/{msgid}"
|
||||||
|
name = "api:mailbox:delete"
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def delete(self, request, platform, msgid):
|
||||||
|
"""Delete items."""
|
||||||
|
mailbox = self.get_mailbox(platform)
|
||||||
|
mailbox.async_delete(msgid)
|
||||||
|
|
||||||
|
|
||||||
|
class MailboxMediaView(MailboxView):
|
||||||
|
"""View to return a media file."""
|
||||||
|
|
||||||
|
url = r"/api/mailbox/media/{platform}/{msgid}"
|
||||||
|
name = "api:asteriskmbox:media"
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def get(self, request, platform, msgid):
|
||||||
|
"""Retrieve media."""
|
||||||
|
mailbox = self.get_mailbox(platform)
|
||||||
|
|
||||||
|
hass = request.app['hass']
|
||||||
|
with suppress(asyncio.CancelledError, asyncio.TimeoutError):
|
||||||
|
with async_timeout.timeout(10, loop=hass.loop):
|
||||||
|
try:
|
||||||
|
stream = yield from mailbox.async_get_media(msgid)
|
||||||
|
except StreamError as err:
|
||||||
|
error_msg = "Error getting media: %s" % (err)
|
||||||
|
_LOGGER.error(error_msg)
|
||||||
|
return web.Response(status=500)
|
||||||
|
if stream:
|
||||||
|
return web.Response(body=stream,
|
||||||
|
content_type=mailbox.media_type)
|
||||||
|
|
||||||
|
return web.Response(status=500)
|
68
homeassistant/components/mailbox/asterisk_mbox.py
Normal file
68
homeassistant/components/mailbox/asterisk_mbox.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
"""
|
||||||
|
Asterisk Voicemail interface.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/mailbox.asteriskvm/
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.components.asterisk_mbox import DOMAIN
|
||||||
|
from homeassistant.components.mailbox import (Mailbox, CONTENT_TYPE_MPEG,
|
||||||
|
StreamError)
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
|
DEPENDENCIES = ['asterisk_mbox']
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SIGNAL_MESSAGE_UPDATE = 'asterisk_mbox.message_updated'
|
||||||
|
SIGNAL_MESSAGE_REQUEST = 'asterisk_mbox.message_request'
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_handler(hass, config, async_add_devices, discovery_info=None):
|
||||||
|
"""Set up the Asterix VM platform."""
|
||||||
|
return AsteriskMailbox(hass, DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
class AsteriskMailbox(Mailbox):
|
||||||
|
"""Asterisk VM Sensor."""
|
||||||
|
|
||||||
|
def __init__(self, hass, name):
|
||||||
|
"""Initialie Asterisk mailbox."""
|
||||||
|
super().__init__(hass, name)
|
||||||
|
async_dispatcher_connect(
|
||||||
|
self.hass, SIGNAL_MESSAGE_UPDATE, self._update_callback)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _update_callback(self, msg):
|
||||||
|
"""Update the message count in HA, if needed."""
|
||||||
|
self.async_update()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def media_type(self):
|
||||||
|
"""Return the supported media type."""
|
||||||
|
return CONTENT_TYPE_MPEG
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_media(self, msgid):
|
||||||
|
"""Return the media blob for the msgid."""
|
||||||
|
from asterisk_mbox import ServerError
|
||||||
|
client = self.hass.data[DOMAIN].client
|
||||||
|
try:
|
||||||
|
return client.mp3(msgid, sync=True)
|
||||||
|
except ServerError as err:
|
||||||
|
raise StreamError(err)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_messages(self):
|
||||||
|
"""Return a list of the current messages."""
|
||||||
|
return self.hass.data[DOMAIN].messages
|
||||||
|
|
||||||
|
def async_delete(self, msgid):
|
||||||
|
"""Delete the specified messages."""
|
||||||
|
client = self.hass.data[DOMAIN].client
|
||||||
|
_LOGGER.info("Deleting: %s", msgid)
|
||||||
|
client.delete(msgid)
|
||||||
|
return True
|
75
homeassistant/components/mailbox/demo.py
Normal file
75
homeassistant/components/mailbox/demo.py
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
"""
|
||||||
|
Asterisk Voicemail interface.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/mailbox.asteriskvm/
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from hashlib import sha1
|
||||||
|
|
||||||
|
import homeassistant.util.dt as dt
|
||||||
|
|
||||||
|
from homeassistant.components.mailbox import (Mailbox, CONTENT_TYPE_MPEG,
|
||||||
|
StreamError)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
DOMAIN = "DemoMailbox"
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_handler(hass, config, discovery_info=None):
|
||||||
|
"""Set up the Demo mailbox."""
|
||||||
|
return DemoMailbox(hass, DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
class DemoMailbox(Mailbox):
|
||||||
|
"""Demo Mailbox."""
|
||||||
|
|
||||||
|
def __init__(self, hass, name):
|
||||||
|
"""Initialize Demo mailbox."""
|
||||||
|
super().__init__(hass, name)
|
||||||
|
self._messages = {}
|
||||||
|
for idx in range(0, 10):
|
||||||
|
msgtime = int(dt.as_timestamp(
|
||||||
|
dt.utcnow()) - 3600 * 24 * (10 - idx))
|
||||||
|
msgtxt = "This is recorded message # %d" % (idx)
|
||||||
|
msgsha = sha1(msgtxt.encode('utf-8')).hexdigest()
|
||||||
|
msg = {"info": {"origtime": msgtime,
|
||||||
|
"callerid": "John Doe <212-555-1212>",
|
||||||
|
"duration": "10"},
|
||||||
|
"text": msgtxt,
|
||||||
|
"sha": msgsha}
|
||||||
|
self._messages[msgsha] = msg
|
||||||
|
|
||||||
|
@property
|
||||||
|
def media_type(self):
|
||||||
|
"""Return the supported media type."""
|
||||||
|
return CONTENT_TYPE_MPEG
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_media(self, msgid):
|
||||||
|
"""Return the media blob for the msgid."""
|
||||||
|
if msgid not in self._messages:
|
||||||
|
raise StreamError("Message not found")
|
||||||
|
|
||||||
|
audio_path = os.path.join(
|
||||||
|
os.path.dirname(__file__), '..', 'tts', 'demo.mp3')
|
||||||
|
with open(audio_path, 'rb') as file:
|
||||||
|
return file.read()
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_get_messages(self):
|
||||||
|
"""Return a list of the current messages."""
|
||||||
|
return sorted(self._messages.values(),
|
||||||
|
key=lambda item: item['info']['origtime'],
|
||||||
|
reverse=True)
|
||||||
|
|
||||||
|
def async_delete(self, msgid):
|
||||||
|
"""Delete the specified messages."""
|
||||||
|
if msgid in self._messages:
|
||||||
|
_LOGGER.info("Deleting: %s", msgid)
|
||||||
|
del self._messages[msgid]
|
||||||
|
self.async_update()
|
||||||
|
return True
|
|
@ -72,6 +72,9 @@ apcaccess==0.0.13
|
||||||
# homeassistant.components.notify.apns
|
# homeassistant.components.notify.apns
|
||||||
apns2==0.1.1
|
apns2==0.1.1
|
||||||
|
|
||||||
|
# homeassistant.components.asterisk_mbox
|
||||||
|
asterisk_mbox==0.4.0
|
||||||
|
|
||||||
# homeassistant.components.light.avion
|
# homeassistant.components.light.avion
|
||||||
# avion==0.7
|
# avion==0.7
|
||||||
|
|
||||||
|
|
1
tests/components/mailbox/__init__.py
Normal file
1
tests/components/mailbox/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"""The tests for mailbox platforms."""
|
116
tests/components/mailbox/test_init.py
Normal file
116
tests/components/mailbox/test_init.py
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
"""The tests for the mailbox component."""
|
||||||
|
import asyncio
|
||||||
|
from hashlib import sha1
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.bootstrap import async_setup_component
|
||||||
|
import homeassistant.components.mailbox as mailbox
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_http_client(hass, test_client):
|
||||||
|
"""Start the Hass HTTP component."""
|
||||||
|
config = {
|
||||||
|
mailbox.DOMAIN: {
|
||||||
|
'platform': 'demo'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hass.loop.run_until_complete(
|
||||||
|
async_setup_component(hass, mailbox.DOMAIN, config))
|
||||||
|
return hass.loop.run_until_complete(test_client(hass.http.app))
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_get_platforms_from_mailbox(mock_http_client):
|
||||||
|
"""Get platforms from mailbox."""
|
||||||
|
url = "/api/mailbox/platforms"
|
||||||
|
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 200
|
||||||
|
result = yield from req.json()
|
||||||
|
assert len(result) == 1 and "DemoMailbox" in result
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_get_messages_from_mailbox(mock_http_client):
|
||||||
|
"""Get messages from mailbox."""
|
||||||
|
url = "/api/mailbox/messages/DemoMailbox"
|
||||||
|
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 200
|
||||||
|
result = yield from req.json()
|
||||||
|
assert len(result) == 10
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_get_media_from_mailbox(mock_http_client):
|
||||||
|
"""Get audio from mailbox."""
|
||||||
|
mp3sha = "3f67c4ea33b37d1710f772a26dd3fb43bb159d50"
|
||||||
|
msgtxt = "This is recorded message # 1"
|
||||||
|
msgsha = sha1(msgtxt.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
url = "/api/mailbox/media/DemoMailbox/%s" % (msgsha)
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 200
|
||||||
|
data = yield from req.read()
|
||||||
|
assert sha1(data).hexdigest() == mp3sha
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_delete_from_mailbox(mock_http_client):
|
||||||
|
"""Get audio from mailbox."""
|
||||||
|
msgtxt1 = "This is recorded message # 1"
|
||||||
|
msgtxt2 = "This is recorded message # 2"
|
||||||
|
msgsha1 = sha1(msgtxt1.encode('utf-8')).hexdigest()
|
||||||
|
msgsha2 = sha1(msgtxt2.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
for msg in [msgsha1, msgsha2]:
|
||||||
|
url = "/api/mailbox/delete/DemoMailbox/%s" % (msg)
|
||||||
|
req = yield from mock_http_client.delete(url)
|
||||||
|
assert req.status == 200
|
||||||
|
|
||||||
|
url = "/api/mailbox/messages/DemoMailbox"
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 200
|
||||||
|
result = yield from req.json()
|
||||||
|
assert len(result) == 8
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_get_messages_from_invalid_mailbox(mock_http_client):
|
||||||
|
"""Get messages from mailbox."""
|
||||||
|
url = "/api/mailbox/messages/mailbox.invalid_mailbox"
|
||||||
|
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 404
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_get_media_from_invalid_mailbox(mock_http_client):
|
||||||
|
"""Get messages from mailbox."""
|
||||||
|
msgsha = "0000000000000000000000000000000000000000"
|
||||||
|
url = "/api/mailbox/media/mailbox.invalid_mailbox/%s" % (msgsha)
|
||||||
|
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 404
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_get_media_from_invalid_msgid(mock_http_client):
|
||||||
|
"""Get messages from mailbox."""
|
||||||
|
msgsha = "0000000000000000000000000000000000000000"
|
||||||
|
url = "/api/mailbox/media/DemoMailbox/%s" % (msgsha)
|
||||||
|
|
||||||
|
req = yield from mock_http_client.get(url)
|
||||||
|
assert req.status == 500
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_delete_from_invalid_mailbox(mock_http_client):
|
||||||
|
"""Get audio from mailbox."""
|
||||||
|
msgsha = "0000000000000000000000000000000000000000"
|
||||||
|
url = "/api/mailbox/delete/mailbox.invalid_mailbox/%s" % (msgsha)
|
||||||
|
|
||||||
|
req = yield from mock_http_client.delete(url)
|
||||||
|
assert req.status == 404
|
Loading…
Add table
Add a link
Reference in a new issue