Hangouts help "page" and little bugfix (#16464)
* add 'default_conversations' parameter * remove the empty message segment at the end of every message * add 'HangoutsHelp' intent * add hangouts/intents.pypull/16474/merge
parent
ff78a5b04b
commit
4efe86327d
|
@ -123,6 +123,7 @@ omit =
|
|||
homeassistant/components/hangouts/const.py
|
||||
homeassistant/components/hangouts/hangouts_bot.py
|
||||
homeassistant/components/hangouts/hangups_utils.py
|
||||
homeassistant/components/hangouts/intents.py
|
||||
homeassistant/components/*/hangouts.py
|
||||
|
||||
homeassistant/components/hdmi_cec.py
|
||||
|
|
|
@ -9,7 +9,9 @@ import logging
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.hangouts.intents import HelpIntent
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.helpers import intent
|
||||
from homeassistant.helpers import dispatcher
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
|
@ -18,12 +20,13 @@ from .const import (
|
|||
EVENT_HANGOUTS_CONNECTED, EVENT_HANGOUTS_CONVERSATIONS_CHANGED,
|
||||
MESSAGE_SCHEMA, SERVICE_SEND_MESSAGE,
|
||||
SERVICE_UPDATE, CONF_SENTENCES, CONF_MATCHERS,
|
||||
CONF_ERROR_SUPPRESSED_CONVERSATIONS, INTENT_SCHEMA, TARGETS_SCHEMA)
|
||||
CONF_ERROR_SUPPRESSED_CONVERSATIONS, INTENT_SCHEMA, TARGETS_SCHEMA,
|
||||
CONF_DEFAULT_CONVERSATIONS, EVENT_HANGOUTS_CONVERSATIONS_RESOLVED,
|
||||
INTENT_HELP)
|
||||
|
||||
# We need an import from .config_flow, without it .config_flow is never loaded.
|
||||
from .config_flow import HangoutsFlowHandler # noqa: F401
|
||||
|
||||
|
||||
REQUIREMENTS = ['hangups==0.4.5']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -33,6 +36,8 @@ CONFIG_SCHEMA = vol.Schema({
|
|||
vol.Optional(CONF_INTENTS, default={}): vol.Schema({
|
||||
cv.string: INTENT_SCHEMA
|
||||
}),
|
||||
vol.Optional(CONF_DEFAULT_CONVERSATIONS, default=[]):
|
||||
[TARGETS_SCHEMA],
|
||||
vol.Optional(CONF_ERROR_SUPPRESSED_CONVERSATIONS, default=[]):
|
||||
[TARGETS_SCHEMA]
|
||||
})
|
||||
|
@ -47,16 +52,23 @@ async def async_setup(hass, config):
|
|||
if config is None:
|
||||
hass.data[DOMAIN] = {
|
||||
CONF_INTENTS: {},
|
||||
CONF_DEFAULT_CONVERSATIONS: [],
|
||||
CONF_ERROR_SUPPRESSED_CONVERSATIONS: [],
|
||||
}
|
||||
return True
|
||||
|
||||
hass.data[DOMAIN] = {
|
||||
CONF_INTENTS: config[CONF_INTENTS],
|
||||
CONF_DEFAULT_CONVERSATIONS: config[CONF_DEFAULT_CONVERSATIONS],
|
||||
CONF_ERROR_SUPPRESSED_CONVERSATIONS:
|
||||
config[CONF_ERROR_SUPPRESSED_CONVERSATIONS],
|
||||
}
|
||||
|
||||
if (hass.data[DOMAIN][CONF_INTENTS] and
|
||||
INTENT_HELP not in hass.data[DOMAIN][CONF_INTENTS]):
|
||||
hass.data[DOMAIN][CONF_INTENTS][INTENT_HELP] = {
|
||||
CONF_SENTENCES: ['HELP']}
|
||||
|
||||
for data in hass.data[DOMAIN][CONF_INTENTS].values():
|
||||
matchers = []
|
||||
for sentence in data[CONF_SENTENCES]:
|
||||
|
@ -82,6 +94,7 @@ async def async_setup_entry(hass, config):
|
|||
hass,
|
||||
config.data.get(CONF_REFRESH_TOKEN),
|
||||
hass.data[DOMAIN][CONF_INTENTS],
|
||||
hass.data[DOMAIN][CONF_DEFAULT_CONVERSATIONS],
|
||||
hass.data[DOMAIN][CONF_ERROR_SUPPRESSED_CONVERSATIONS])
|
||||
hass.data[DOMAIN][CONF_BOT] = bot
|
||||
except GoogleAuthError as exception:
|
||||
|
@ -96,11 +109,12 @@ async def async_setup_entry(hass, config):
|
|||
dispatcher.async_dispatcher_connect(
|
||||
hass,
|
||||
EVENT_HANGOUTS_CONVERSATIONS_CHANGED,
|
||||
bot.async_update_conversation_commands)
|
||||
bot.async_resolve_conversations)
|
||||
|
||||
dispatcher.async_dispatcher_connect(
|
||||
hass,
|
||||
EVENT_HANGOUTS_CONVERSATIONS_CHANGED,
|
||||
bot.async_handle_update_error_suppressed_conversations)
|
||||
EVENT_HANGOUTS_CONVERSATIONS_RESOLVED,
|
||||
bot.async_update_conversation_commands)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||
bot.async_handle_hass_stop)
|
||||
|
@ -116,6 +130,8 @@ async def async_setup_entry(hass, config):
|
|||
async_handle_update_users_and_conversations,
|
||||
schema=vol.Schema({}))
|
||||
|
||||
intent.async_register(hass, HelpIntent(hass))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -24,10 +24,13 @@ CONF_INTENT_TYPE = 'intent_type'
|
|||
CONF_SENTENCES = 'sentences'
|
||||
CONF_MATCHERS = 'matchers'
|
||||
|
||||
INTENT_HELP = 'HangoutsHelp'
|
||||
|
||||
EVENT_HANGOUTS_CONNECTED = 'hangouts_connected'
|
||||
EVENT_HANGOUTS_DISCONNECTED = 'hangouts_disconnected'
|
||||
EVENT_HANGOUTS_USERS_CHANGED = 'hangouts_users_changed'
|
||||
EVENT_HANGOUTS_CONVERSATIONS_CHANGED = 'hangouts_conversations_changed'
|
||||
EVENT_HANGOUTS_CONVERSATIONS_RESOLVED = 'hangouts_conversations_resolved'
|
||||
EVENT_HANGOUTS_MESSAGE_RECEIVED = 'hangouts_message_received'
|
||||
|
||||
CONF_CONVERSATION_ID = 'id'
|
||||
|
|
|
@ -8,7 +8,7 @@ from .const import (
|
|||
EVENT_HANGOUTS_CONNECTED, EVENT_HANGOUTS_CONVERSATIONS_CHANGED,
|
||||
EVENT_HANGOUTS_DISCONNECTED, EVENT_HANGOUTS_MESSAGE_RECEIVED,
|
||||
CONF_MATCHERS, CONF_CONVERSATION_ID,
|
||||
CONF_CONVERSATION_NAME)
|
||||
CONF_CONVERSATION_NAME, EVENT_HANGOUTS_CONVERSATIONS_RESOLVED, INTENT_HELP)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -16,7 +16,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||
class HangoutsBot:
|
||||
"""The Hangouts Bot."""
|
||||
|
||||
def __init__(self, hass, refresh_token, intents, error_suppressed_convs):
|
||||
def __init__(self, hass, refresh_token, intents,
|
||||
default_convs, error_suppressed_convs):
|
||||
"""Set up the client."""
|
||||
self.hass = hass
|
||||
self._connected = False
|
||||
|
@ -29,6 +30,8 @@ class HangoutsBot:
|
|||
self._client = None
|
||||
self._user_list = None
|
||||
self._conversation_list = None
|
||||
self._default_convs = default_convs
|
||||
self._default_conv_ids = None
|
||||
self._error_suppressed_convs = error_suppressed_convs
|
||||
self._error_suppressed_conv_ids = None
|
||||
|
||||
|
@ -51,7 +54,7 @@ class HangoutsBot:
|
|||
return conv
|
||||
return None
|
||||
|
||||
def async_update_conversation_commands(self, _):
|
||||
def async_update_conversation_commands(self):
|
||||
"""Refresh the commands for every conversation."""
|
||||
self._conversation_intents = {}
|
||||
|
||||
|
@ -63,6 +66,8 @@ class HangoutsBot:
|
|||
if conv_id is not None:
|
||||
conversations.append(conv_id)
|
||||
data['_' + CONF_CONVERSATIONS] = conversations
|
||||
elif self._default_conv_ids:
|
||||
data['_' + CONF_CONVERSATIONS] = self._default_conv_ids
|
||||
else:
|
||||
data['_' + CONF_CONVERSATIONS] = \
|
||||
[conv.id_ for conv in self._conversation_list.get_all()]
|
||||
|
@ -81,13 +86,22 @@ class HangoutsBot:
|
|||
self._conversation_list.on_event.add_observer(
|
||||
self._async_handle_conversation_event)
|
||||
|
||||
def async_handle_update_error_suppressed_conversations(self, _):
|
||||
"""Resolve the list of error suppressed conversations."""
|
||||
def async_resolve_conversations(self, _):
|
||||
"""Resolve the list of default and error suppressed conversations."""
|
||||
self._default_conv_ids = []
|
||||
self._error_suppressed_conv_ids = []
|
||||
|
||||
for conversation in self._default_convs:
|
||||
conv_id = self._resolve_conversation_id(conversation)
|
||||
if conv_id is not None:
|
||||
self._default_conv_ids.append(conv_id)
|
||||
|
||||
for conversation in self._error_suppressed_convs:
|
||||
conv_id = self._resolve_conversation_id(conversation)
|
||||
if conv_id is not None:
|
||||
self._error_suppressed_conv_ids.append(conv_id)
|
||||
dispatcher.async_dispatcher_send(self.hass,
|
||||
EVENT_HANGOUTS_CONVERSATIONS_RESOLVED)
|
||||
|
||||
async def _async_handle_conversation_event(self, event):
|
||||
from hangups import ChatMessageEvent
|
||||
|
@ -112,7 +126,8 @@ class HangoutsBot:
|
|||
if intents is not None:
|
||||
is_error = False
|
||||
try:
|
||||
intent_result = await self._async_process(intents, message)
|
||||
intent_result = await self._async_process(intents, message,
|
||||
conv_id)
|
||||
except (intent.UnknownIntent, intent.IntentHandleError) as err:
|
||||
is_error = True
|
||||
intent_result = intent.IntentResponse()
|
||||
|
@ -133,7 +148,7 @@ class HangoutsBot:
|
|||
[{'text': message, 'parse_str': True}],
|
||||
[{CONF_CONVERSATION_ID: conv_id}])
|
||||
|
||||
async def _async_process(self, intents, text):
|
||||
async def _async_process(self, intents, text, conv_id):
|
||||
"""Detect a matching intent."""
|
||||
for intent_type, data in intents.items():
|
||||
for matcher in data.get(CONF_MATCHERS, []):
|
||||
|
@ -141,12 +156,15 @@ class HangoutsBot:
|
|||
|
||||
if not match:
|
||||
continue
|
||||
if intent_type == INTENT_HELP:
|
||||
return await self.hass.helpers.intent.async_handle(
|
||||
DOMAIN, intent_type,
|
||||
{'conv_id': {'value': conv_id}}, text)
|
||||
|
||||
response = await self.hass.helpers.intent.async_handle(
|
||||
return await self.hass.helpers.intent.async_handle(
|
||||
DOMAIN, intent_type,
|
||||
{key: {'value': value} for key, value
|
||||
in match.groupdict().items()}, text)
|
||||
return response
|
||||
{key: {'value': value}
|
||||
for key, value in match.groupdict().items()}, text)
|
||||
|
||||
async def async_connect(self):
|
||||
"""Login to the Google Hangouts."""
|
||||
|
@ -204,15 +222,16 @@ class HangoutsBot:
|
|||
from hangups import ChatMessageSegment, hangouts_pb2
|
||||
messages = []
|
||||
for segment in message:
|
||||
if messages:
|
||||
messages.append(ChatMessageSegment('',
|
||||
segment_type=hangouts_pb2.
|
||||
SEGMENT_TYPE_LINE_BREAK))
|
||||
if 'parse_str' in segment and segment['parse_str']:
|
||||
messages.extend(ChatMessageSegment.from_str(segment['text']))
|
||||
else:
|
||||
if 'parse_str' in segment:
|
||||
del segment['parse_str']
|
||||
messages.append(ChatMessageSegment(**segment))
|
||||
messages.append(ChatMessageSegment('',
|
||||
segment_type=hangouts_pb2.
|
||||
SEGMENT_TYPE_LINE_BREAK))
|
||||
|
||||
if not messages:
|
||||
return False
|
||||
|
@ -247,3 +266,7 @@ class HangoutsBot:
|
|||
async def async_handle_update_users_and_conversations(self, _=None):
|
||||
"""Handle the update_users_and_conversations service."""
|
||||
await self._async_list_conversations()
|
||||
|
||||
def get_intents(self, conv_id):
|
||||
"""Return the intents for a specific conversation."""
|
||||
return self._conversation_intents.get(conv_id)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
"""Intents for the hangouts component."""
|
||||
from homeassistant.helpers import intent
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .const import INTENT_HELP, DOMAIN, CONF_BOT
|
||||
|
||||
|
||||
class HelpIntent(intent.IntentHandler):
|
||||
"""Handle Help intents."""
|
||||
|
||||
intent_type = INTENT_HELP
|
||||
slot_schema = {
|
||||
'conv_id': cv.string
|
||||
}
|
||||
|
||||
def __init__(self, hass):
|
||||
"""Set up the intent."""
|
||||
self.hass = hass
|
||||
|
||||
async def async_handle(self, intent_obj):
|
||||
"""Handle the intent."""
|
||||
slots = self.async_validate_slots(intent_obj.slots)
|
||||
conv_id = slots['conv_id']['value']
|
||||
|
||||
intents = self.hass.data[DOMAIN][CONF_BOT].get_intents(conv_id)
|
||||
response = intent_obj.create_response()
|
||||
help_text = "I understand the following sentences:"
|
||||
for intent_data in intents.values():
|
||||
for sentence in intent_data['sentences']:
|
||||
help_text += "\n'{}'".format(sentence)
|
||||
response.async_set_speech(help_text)
|
||||
|
||||
return response
|
Loading…
Reference in New Issue