From bc8d323bdd6a66fba254d20b0339603fe6bd63ec Mon Sep 17 00:00:00 2001 From: Tommy Jonsson Date: Tue, 25 Sep 2018 15:04:43 +0200 Subject: [PATCH] Add image support to hangouts notifications (#16560) * add image to services.yaml * add image support Add image support to hangouts notification. * fix indents * fix line length * Add data schema forgot schema * fix linelength * use is_allowed_path and better file error handling * elif not else if * fix logger error * fixes * fix travis errors/warnings * remove trailing whitespace * fix travis pylint naming * new async style * removed unused async_timeout * change to image_file/url (#3) * change to image_file/url * removed whitespace * forgot to remove unused urlparse import * image_file/url in service help --- homeassistant/components/hangouts/const.py | 10 +++- .../components/hangouts/hangouts_bot.py | 55 +++++++++++++++++-- .../components/hangouts/services.yaml | 6 +- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/hangouts/const.py b/homeassistant/components/hangouts/const.py index caae0de169b..5a527fae260 100644 --- a/homeassistant/components/hangouts/const.py +++ b/homeassistant/components/hangouts/const.py @@ -3,7 +3,8 @@ import logging import voluptuous as vol -from homeassistant.components.notify import ATTR_MESSAGE, ATTR_TARGET +from homeassistant.components.notify \ + import ATTR_MESSAGE, ATTR_TARGET, ATTR_DATA import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger('homeassistant.components.hangouts') @@ -56,10 +57,15 @@ MESSAGE_SEGMENT_SCHEMA = vol.Schema({ vol.Optional('parse_str'): cv.boolean, vol.Optional('link_target'): cv.string }) +MESSAGE_DATA_SCHEMA = vol.Schema({ + vol.Optional('image_file'): cv.string, + vol.Optional('image_url'): cv.string +}) MESSAGE_SCHEMA = vol.Schema({ vol.Required(ATTR_TARGET): [TARGETS_SCHEMA], - vol.Required(ATTR_MESSAGE): [MESSAGE_SEGMENT_SCHEMA] + vol.Required(ATTR_MESSAGE): [MESSAGE_SEGMENT_SCHEMA], + vol.Optional(ATTR_DATA): MESSAGE_DATA_SCHEMA }) INTENT_SCHEMA = vol.All( diff --git a/homeassistant/components/hangouts/hangouts_bot.py b/homeassistant/components/hangouts/hangouts_bot.py index 7edc8898c8c..8747bff9ba7 100644 --- a/homeassistant/components/hangouts/hangouts_bot.py +++ b/homeassistant/components/hangouts/hangouts_bot.py @@ -1,10 +1,13 @@ """The Hangouts Bot.""" +import io import logging - +import asyncio +import aiohttp +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers import dispatcher, intent from .const import ( - ATTR_MESSAGE, ATTR_TARGET, CONF_CONVERSATIONS, DOMAIN, + ATTR_MESSAGE, ATTR_TARGET, ATTR_DATA, CONF_CONVERSATIONS, DOMAIN, EVENT_HANGOUTS_CONNECTED, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, EVENT_HANGOUTS_DISCONNECTED, EVENT_HANGOUTS_MESSAGE_RECEIVED, CONF_MATCHERS, CONF_CONVERSATION_ID, @@ -146,7 +149,8 @@ class HangoutsBot: is_error and conv_id in self._error_suppressed_conv_ids): await self._async_send_message( [{'text': message, 'parse_str': True}], - [{CONF_CONVERSATION_ID: conv_id}]) + [{CONF_CONVERSATION_ID: conv_id}], + None) async def _async_process(self, intents, text, conv_id): """Detect a matching intent.""" @@ -203,7 +207,7 @@ class HangoutsBot: """Run once when Home Assistant stops.""" await self.async_disconnect() - async def _async_send_message(self, message, targets): + async def _async_send_message(self, message, targets, data): conversations = [] for target in targets: conversation = None @@ -233,10 +237,48 @@ class HangoutsBot: del segment['parse_str'] messages.append(ChatMessageSegment(**segment)) + image_file = None + if data: + if data.get('image_url'): + uri = data.get('image_url') + try: + websession = async_get_clientsession(self.hass) + async with websession.get(uri, timeout=5) as response: + if response.status != 200: + _LOGGER.error( + 'Fetch image failed, %s, %s', + response.status, + response + ) + image_file = None + else: + image_data = await response.read() + image_file = io.BytesIO(image_data) + image_file.name = "image.png" + except (asyncio.TimeoutError, aiohttp.ClientError) as error: + _LOGGER.error( + 'Failed to fetch image, %s', + type(error) + ) + image_file = None + elif data.get('image_file'): + uri = data.get('image_file') + if self.hass.config.is_allowed_path(uri): + try: + image_file = open(uri, 'rb') + except IOError as error: + _LOGGER.error( + 'Image file I/O error(%s): %s', + error.errno, + error.strerror + ) + else: + _LOGGER.error('Path "%s" not allowed', uri) + if not messages: return False for conv in conversations: - await conv.send_message(messages) + await conv.send_message(messages, image_file) async def _async_list_conversations(self): import hangups @@ -261,7 +303,8 @@ class HangoutsBot: async def async_handle_send_message(self, service): """Handle the send_message service.""" await self._async_send_message(service.data[ATTR_MESSAGE], - service.data[ATTR_TARGET]) + service.data[ATTR_TARGET], + service.data[ATTR_DATA]) async def async_handle_update_users_and_conversations(self, _=None): """Handle the update_users_and_conversations service.""" diff --git a/homeassistant/components/hangouts/services.yaml b/homeassistant/components/hangouts/services.yaml index 5d314bc2479..ded324d2de9 100644 --- a/homeassistant/components/hangouts/services.yaml +++ b/homeassistant/components/hangouts/services.yaml @@ -9,4 +9,8 @@ send_message: example: '[{"id": "UgxrXzVrARmjx_C6AZx4AaABAagBo-6UCw"}, {"name": "Test Conversation"}]' message: description: List of message segments, only the "text" field is required in every segment. [Required] - example: '[{"text":"test", "is_bold": false, "is_italic": false, "is_strikethrough": false, "is_underline": false, "parse_str": false, "link_target": "http://google.com"}, ...]' \ No newline at end of file + example: '[{"text":"test", "is_bold": false, "is_italic": false, "is_strikethrough": false, "is_underline": false, "parse_str": false, "link_target": "http://google.com"}, ...]' + data: + description: Other options ['image_file' / 'image_url'] + example: '{ "image_file": "file" }' or '{ "image_url": "url" }' +