Remove Google Chat/Hangouts integration (#82645)

This commit is contained in:
Franck Nijhof 2022-11-24 18:33:13 +01:00 committed by GitHub
parent 8577310e6d
commit 285aff154d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 0 additions and 2184 deletions

View File

@ -483,11 +483,6 @@ omit =
homeassistant/components/habitica/__init__.py
homeassistant/components/habitica/const.py
homeassistant/components/habitica/sensor.py
homeassistant/components/hangouts/__init__.py
homeassistant/components/hangouts/hangouts_bot.py
homeassistant/components/hangouts/hangups_utils.py
homeassistant/components/hangouts/intents.py
homeassistant/components/hangouts/notify.py
homeassistant/components/harman_kardon_avr/media_player.py
homeassistant/components/harmony/const.py
homeassistant/components/harmony/data.py

View File

@ -14,7 +14,6 @@
"google",
"nest",
"cast",
"hangouts",
"dialogflow"
]
}

View File

@ -1,160 +0,0 @@
"""Support for Hangouts."""
import logging
from hangups.auth import GoogleAuthError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.conversation.util import create_matcher
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
from homeassistant.helpers import dispatcher, intent
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType
# We need an import from .config_flow, without it .config_flow is never loaded.
from .config_flow import HangoutsFlowHandler # noqa: F401
from .const import (
CONF_BOT,
CONF_DEFAULT_CONVERSATIONS,
CONF_ERROR_SUPPRESSED_CONVERSATIONS,
CONF_INTENTS,
CONF_MATCHERS,
CONF_REFRESH_TOKEN,
CONF_SENTENCES,
DOMAIN,
EVENT_HANGOUTS_CONNECTED,
EVENT_HANGOUTS_CONVERSATIONS_CHANGED,
EVENT_HANGOUTS_CONVERSATIONS_RESOLVED,
INTENT_HELP,
INTENT_SCHEMA,
MESSAGE_SCHEMA,
SERVICE_RECONNECT,
SERVICE_SEND_MESSAGE,
SERVICE_UPDATE,
TARGETS_SCHEMA,
)
from .hangouts_bot import HangoutsBot
from .intents import HelpIntent
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: 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
],
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Hangouts bot component."""
if (conf := config.get(DOMAIN)) is None:
hass.data[DOMAIN] = {
CONF_INTENTS: {},
CONF_DEFAULT_CONVERSATIONS: [],
CONF_ERROR_SUPPRESSED_CONVERSATIONS: [],
}
return True
hass.data[DOMAIN] = {
CONF_INTENTS: conf[CONF_INTENTS],
CONF_DEFAULT_CONVERSATIONS: conf[CONF_DEFAULT_CONVERSATIONS],
CONF_ERROR_SUPPRESSED_CONVERSATIONS: conf[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]:
matchers.append(create_matcher(sentence))
data[CONF_MATCHERS] = matchers
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}
)
)
return True
async def async_setup_entry(hass: HomeAssistant, config: ConfigEntry) -> bool:
"""Set up a config entry."""
try:
bot = HangoutsBot(
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:
_LOGGER.error("Hangouts failed to log in: %s", str(exception))
return False
dispatcher.async_dispatcher_connect(
hass, EVENT_HANGOUTS_CONNECTED, bot.async_handle_update_users_and_conversations
)
dispatcher.async_dispatcher_connect(
hass, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, bot.async_resolve_conversations
)
dispatcher.async_dispatcher_connect(
hass,
EVENT_HANGOUTS_CONVERSATIONS_RESOLVED,
bot.async_update_conversation_commands,
)
config.async_on_unload(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, bot.async_handle_hass_stop)
)
await bot.async_connect()
hass.services.async_register(
DOMAIN,
SERVICE_SEND_MESSAGE,
bot.async_handle_send_message,
schema=MESSAGE_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_UPDATE,
bot.async_handle_update_users_and_conversations,
schema=vol.Schema({}),
)
hass.services.async_register(
DOMAIN, SERVICE_RECONNECT, bot.async_handle_reconnect, schema=vol.Schema({})
)
intent.async_register(hass, HelpIntent(hass))
return True
async def async_unload_entry(hass: HomeAssistant, _: ConfigEntry) -> bool:
"""Unload a config entry."""
bot = hass.data[DOMAIN].pop(CONF_BOT)
await bot.async_disconnect()
return True

View File

@ -1,111 +0,0 @@
"""Config flow to configure Google Hangouts."""
import functools
from hangups import get_auth
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from .const import CONF_2FA, CONF_AUTH_CODE, CONF_REFRESH_TOKEN, DOMAIN
from .hangups_utils import (
Google2FAError,
GoogleAuthError,
HangoutsCredentials,
HangoutsRefreshToken,
)
class HangoutsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow Google Hangouts."""
VERSION = 1
def __init__(self):
"""Initialize Google Hangouts config flow."""
self._credentials = None
self._refresh_token = None
async def async_step_user(self, user_input=None):
"""Handle a flow start."""
errors = {}
self._async_abort_entries_match()
if user_input is not None:
user_email = user_input[CONF_EMAIL]
user_password = user_input[CONF_PASSWORD]
user_auth_code = user_input.get(CONF_AUTH_CODE)
manual_login = user_auth_code is not None
user_pin = None
self._credentials = HangoutsCredentials(
user_email, user_password, user_pin, user_auth_code
)
self._refresh_token = HangoutsRefreshToken(None)
try:
await self.hass.async_add_executor_job(
functools.partial(
get_auth,
self._credentials,
self._refresh_token,
manual_login=manual_login,
)
)
return await self.async_step_final()
except GoogleAuthError as err:
if isinstance(err, Google2FAError):
return await self.async_step_2fa()
msg = str(err)
if msg == "Unknown verification code input":
errors["base"] = "invalid_2fa_method"
else:
errors["base"] = "invalid_login"
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_EMAIL): str,
vol.Required(CONF_PASSWORD): str,
vol.Optional(CONF_AUTH_CODE): str,
}
),
errors=errors,
)
async def async_step_2fa(self, user_input=None):
"""Handle the 2fa step, if needed."""
errors = {}
if user_input is not None:
self._credentials.set_verification_code(user_input[CONF_2FA])
try:
await self.hass.async_add_executor_job(
get_auth, self._credentials, self._refresh_token
)
return await self.async_step_final()
except GoogleAuthError:
errors["base"] = "invalid_2fa"
return self.async_show_form(
step_id=CONF_2FA,
data_schema=vol.Schema({vol.Required(CONF_2FA): str}),
errors=errors,
)
async def async_step_final(self):
"""Handle the final step, create the config entry."""
return self.async_create_entry(
title=self._credentials.get_email(),
data={
CONF_EMAIL: self._credentials.get_email(),
CONF_REFRESH_TOKEN: self._refresh_token.get(),
},
)
async def async_step_import(self, _):
"""Handle a flow import."""
return await self.async_step_user()

View File

@ -1,80 +0,0 @@
"""Constants for Google Hangouts Component."""
import voluptuous as vol
from homeassistant.components.notify import ATTR_DATA, ATTR_MESSAGE, ATTR_TARGET
import homeassistant.helpers.config_validation as cv
DOMAIN = "hangouts"
CONF_2FA = "2fa"
CONF_AUTH_CODE = "authorization_code"
CONF_REFRESH_TOKEN = "refresh_token"
CONF_BOT = "bot"
CONF_CONVERSATIONS = "conversations"
CONF_DEFAULT_CONVERSATIONS = "default_conversations"
CONF_ERROR_SUPPRESSED_CONVERSATIONS = "error_suppressed_conversations"
CONF_INTENTS = "intents"
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"
CONF_CONVERSATION_NAME = "name"
SERVICE_SEND_MESSAGE = "send_message"
SERVICE_UPDATE = "update"
SERVICE_RECONNECT = "reconnect"
TARGETS_SCHEMA = vol.All(
vol.Schema(
{
vol.Exclusive(CONF_CONVERSATION_ID, "id or name"): cv.string,
vol.Exclusive(CONF_CONVERSATION_NAME, "id or name"): cv.string,
}
),
cv.has_at_least_one_key(CONF_CONVERSATION_ID, CONF_CONVERSATION_NAME),
)
MESSAGE_SEGMENT_SCHEMA = vol.Schema(
{
vol.Required("text"): cv.string,
vol.Optional("is_bold"): cv.boolean,
vol.Optional("is_italic"): cv.boolean,
vol.Optional("is_strikethrough"): cv.boolean,
vol.Optional("is_underline"): cv.boolean,
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.Optional(ATTR_DATA): MESSAGE_DATA_SCHEMA,
}
)
INTENT_SCHEMA = vol.All(
# Basic Schema
vol.Schema(
{
vol.Required(CONF_SENTENCES): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_CONVERSATIONS): [TARGETS_SCHEMA],
}
)
)

View File

@ -1,361 +0,0 @@
"""The Hangouts Bot."""
from __future__ import annotations
import asyncio
from contextlib import suppress
from http import HTTPStatus
import io
import logging
import aiohttp
import hangups
from hangups import ChatMessageEvent, ChatMessageSegment, Client, get_auth, hangouts_pb2
from homeassistant.core import ServiceCall, callback
from homeassistant.helpers import dispatcher, intent
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import (
ATTR_DATA,
ATTR_MESSAGE,
ATTR_TARGET,
CONF_CONVERSATION_ID,
CONF_CONVERSATION_NAME,
CONF_CONVERSATIONS,
CONF_MATCHERS,
DOMAIN,
EVENT_HANGOUTS_CONNECTED,
EVENT_HANGOUTS_CONVERSATIONS_CHANGED,
EVENT_HANGOUTS_CONVERSATIONS_RESOLVED,
EVENT_HANGOUTS_DISCONNECTED,
EVENT_HANGOUTS_MESSAGE_RECEIVED,
INTENT_HELP,
)
from .hangups_utils import HangoutsCredentials, HangoutsRefreshToken
_LOGGER = logging.getLogger(__name__)
class HangoutsBot:
"""The Hangouts Bot."""
def __init__(
self, hass, refresh_token, intents, default_convs, error_suppressed_convs
):
"""Set up the client."""
self.hass = hass
self._connected = False
self._refresh_token = refresh_token
self._intents = intents
self._conversation_intents = None
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
dispatcher.async_dispatcher_connect(
self.hass,
EVENT_HANGOUTS_MESSAGE_RECEIVED,
self._async_handle_conversation_message,
)
def _resolve_conversation_id(self, obj):
if CONF_CONVERSATION_ID in obj:
return obj[CONF_CONVERSATION_ID]
if CONF_CONVERSATION_NAME in obj:
conv = self._resolve_conversation_name(obj[CONF_CONVERSATION_NAME])
if conv is not None:
return conv.id_
return None
def _resolve_conversation_name(self, name):
for conv in self._conversation_list.get_all():
if conv.name == name:
return conv
return None
@callback
def async_update_conversation_commands(self):
"""Refresh the commands for every conversation."""
self._conversation_intents = {}
for intent_type, data in self._intents.items():
if data.get(CONF_CONVERSATIONS):
conversations = []
for conversation in data.get(CONF_CONVERSATIONS):
conv_id = self._resolve_conversation_id(conversation)
if conv_id is not None:
conversations.append(conv_id)
data[f"_{CONF_CONVERSATIONS}"] = conversations
elif self._default_conv_ids:
data[f"_{CONF_CONVERSATIONS}"] = self._default_conv_ids
else:
data[f"_{CONF_CONVERSATIONS}"] = [
conv.id_ for conv in self._conversation_list.get_all()
]
for conv_id in data[f"_{CONF_CONVERSATIONS}"]:
if conv_id not in self._conversation_intents:
self._conversation_intents[conv_id] = {}
self._conversation_intents[conv_id][intent_type] = data
with suppress(ValueError):
self._conversation_list.on_event.remove_observer(
self._async_handle_conversation_event
)
self._conversation_list.on_event.add_observer(
self._async_handle_conversation_event
)
@callback
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):
if isinstance(event, ChatMessageEvent):
dispatcher.async_dispatcher_send(
self.hass,
EVENT_HANGOUTS_MESSAGE_RECEIVED,
event.conversation_id,
event.user_id,
event,
)
async def _async_handle_conversation_message(self, conv_id, user_id, event):
"""Handle a message sent to a conversation."""
user = self._user_list.get_user(user_id)
if user.is_self:
return
message = event.text
_LOGGER.debug("Handling message '%s' from %s", message, user.full_name)
intents = self._conversation_intents.get(conv_id)
if intents is not None:
is_error = False
try:
intent_result = await self._async_process(intents, message, conv_id)
except (intent.UnknownIntent, intent.IntentHandleError) as err:
is_error = True
intent_result = intent.IntentResponse()
intent_result.async_set_speech(str(err))
if intent_result is None:
is_error = True
intent_result = intent.IntentResponse()
intent_result.async_set_speech("Sorry, I didn't understand that")
message = (
intent_result.as_dict().get("speech", {}).get("plain", {}).get("speech")
)
if (message is not None) and not (
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}],
None,
)
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, []):
if not (match := matcher.match(text)):
continue
if intent_type == INTENT_HELP:
return await intent.async_handle(
self.hass,
DOMAIN,
intent_type,
{"conv_id": {"value": conv_id}},
text,
)
return await intent.async_handle(
self.hass,
DOMAIN,
intent_type,
{"conv_id": {"value": conv_id}}
| {
key: {"value": value}
for key, value in match.groupdict().items()
},
text,
)
async def async_connect(self):
"""Login to the Google Hangouts."""
session = await self.hass.async_add_executor_job(
get_auth,
HangoutsCredentials(None, None, None),
HangoutsRefreshToken(self._refresh_token),
)
self._client = Client(session)
self._client.on_connect.add_observer(self._on_connect)
self._client.on_disconnect.add_observer(self._on_disconnect)
self.hass.loop.create_task(self._client.connect())
def _on_connect(self):
_LOGGER.debug("Connected!")
self._connected = True
dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_CONNECTED)
async def _on_disconnect(self):
"""Handle disconnecting."""
if self._connected:
_LOGGER.debug("Connection lost! Reconnect")
await self.async_connect()
else:
dispatcher.async_dispatcher_send(self.hass, EVENT_HANGOUTS_DISCONNECTED)
async def async_disconnect(self):
"""Disconnect the client if it is connected."""
if self._connected:
self._connected = False
await self._client.disconnect()
async def async_handle_hass_stop(self, _):
"""Run once when Home Assistant stops."""
await self.async_disconnect()
async def _async_send_message(self, message, targets, data):
conversations = []
for target in targets:
conversation = None
if CONF_CONVERSATION_ID in target:
conversation = self._conversation_list.get(target[CONF_CONVERSATION_ID])
elif CONF_CONVERSATION_NAME in target:
conversation = self._resolve_conversation_name(
target[CONF_CONVERSATION_NAME]
)
if conversation is not None:
conversations.append(conversation)
if not conversations:
return False
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))
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 != HTTPStatus.OK:
_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:
# pylint: disable=consider-using-with
image_file = open(uri, "rb")
except OSError 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, image_file)
async def _async_list_conversations(self):
(
self._user_list,
self._conversation_list,
) = await hangups.build_user_conversation_list(self._client)
conversations = {}
for i, conv in enumerate(self._conversation_list.get_all()):
users_in_conversation = []
for user in conv.users:
users_in_conversation.append(user.full_name)
conversations[str(i)] = {
CONF_CONVERSATION_ID: str(conv.id_),
CONF_CONVERSATION_NAME: conv.name,
"users": users_in_conversation,
}
self.hass.states.async_set(
f"{DOMAIN}.conversations",
len(self._conversation_list.get_all()),
attributes=conversations,
)
dispatcher.async_dispatcher_send(
self.hass, EVENT_HANGOUTS_CONVERSATIONS_CHANGED, conversations
)
async def async_handle_send_message(self, service: ServiceCall) -> None:
"""Handle the send_message service."""
await self._async_send_message(
service.data[ATTR_MESSAGE],
service.data[ATTR_TARGET],
service.data.get(ATTR_DATA, {}),
)
async def async_handle_update_users_and_conversations(
self, service: ServiceCall | None = None
) -> None:
"""Handle the update_users_and_conversations service."""
await self._async_list_conversations()
async def async_handle_reconnect(self, service: ServiceCall | None = None) -> None:
"""Handle the reconnect service."""
await self.async_disconnect()
await self.async_connect()
def get_intents(self, conv_id):
"""Return the intents for a specific conversation."""
return self._conversation_intents.get(conv_id)

View File

@ -1,96 +0,0 @@
"""Utils needed for Google Hangouts."""
from hangups import CredentialsPrompt, GoogleAuthError, RefreshTokenCache
class Google2FAError(GoogleAuthError):
"""A Google authentication request failed."""
class HangoutsCredentials(CredentialsPrompt):
"""Google account credentials.
This implementation gets the user data as params.
"""
def __init__(self, email, password, pin=None, auth_code=None):
"""Google account credentials.
:param email: Google account email address.
:param password: Google account password.
:param pin: Google account verification code.
"""
self._email = email
self._password = password
self._pin = pin
self._auth_code = auth_code
def get_email(self):
"""Return email.
:return: Google account email address.
"""
return self._email
def get_password(self):
"""Return password.
:return: Google account password.
"""
return self._password
def get_verification_code(self):
"""Return the verification code.
:return: Google account verification code.
"""
if self._pin is None:
raise Google2FAError()
return self._pin
def set_verification_code(self, pin):
"""Set the verification code.
:param pin: Google account verification code.
"""
self._pin = pin
def get_authorization_code(self):
"""Return the oauth authorization code.
:return: Google oauth code.
"""
return self._auth_code
def set_authorization_code(self, code):
"""Set the google oauth authorization code.
:param code: Oauth code returned after authentication with google.
"""
self._auth_code = code
class HangoutsRefreshToken(RefreshTokenCache):
"""Memory-based cache for refresh token."""
def __init__(self, token):
"""Memory-based cache for refresh token.
:param token: Initial refresh token.
"""
super().__init__("")
self._token = token
def get(self):
"""Get cached refresh token.
:return: Cached refresh token.
"""
return self._token
def set(self, refresh_token):
"""Cache a refresh token.
:param refresh_token: Refresh token to cache.
"""
self._token = refresh_token

View File

@ -1,31 +0,0 @@
"""Intents for the Hangouts component."""
from homeassistant.helpers import intent
import homeassistant.helpers.config_validation as cv
from .const import CONF_BOT, DOMAIN, INTENT_HELP
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 += f"\n'{sentence}'"
response.async_set_speech(help_text)
return response

View File

@ -1,10 +0,0 @@
{
"domain": "hangouts",
"name": "Google Chat",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/hangouts",
"requirements": ["hangups==0.4.18"],
"codeowners": [],
"iot_class": "cloud_push",
"loggers": ["hangups", "urwid"]
}

View File

@ -1,57 +0,0 @@
"""Support for Hangouts notifications."""
import voluptuous as vol
from homeassistant.components.notify import (
ATTR_DATA,
ATTR_MESSAGE,
ATTR_TARGET,
PLATFORM_SCHEMA,
BaseNotificationService,
)
from .const import (
CONF_DEFAULT_CONVERSATIONS,
DOMAIN,
SERVICE_SEND_MESSAGE,
TARGETS_SCHEMA,
)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Required(CONF_DEFAULT_CONVERSATIONS): [TARGETS_SCHEMA]}
)
def get_service(hass, config, discovery_info=None):
"""Get the Hangouts notification service."""
return HangoutsNotificationService(config.get(CONF_DEFAULT_CONVERSATIONS))
class HangoutsNotificationService(BaseNotificationService):
"""Send Notifications to Hangouts conversations."""
def __init__(self, default_conversations):
"""Set up the notification service."""
self._default_conversations = default_conversations
def send_message(self, message="", **kwargs):
"""Send the message to the Google Hangouts server."""
target_conversations = None
if ATTR_TARGET in kwargs:
target_conversations = []
for target in kwargs.get(ATTR_TARGET):
target_conversations.append({"id": target})
else:
target_conversations = self._default_conversations
messages = []
if "title" in kwargs:
messages.append({"text": kwargs["title"], "is_bold": True})
messages.append({"text": message, "parse_str": True})
service_data = {ATTR_TARGET: target_conversations, ATTR_MESSAGE: messages}
if kwargs[ATTR_DATA]:
service_data[ATTR_DATA] = kwargs[ATTR_DATA]
return self.hass.services.call(
DOMAIN, SERVICE_SEND_MESSAGE, service_data=service_data
)

View File

@ -1,32 +0,0 @@
update:
name: Update
description: Updates the list of conversations.
send_message:
name: Send message
description: Send a notification to a specific target.
fields:
target:
name: Target
description: List of targets with id or name.
required: true
example: '[{"id": "UgxrXzVrARmjx_C6AZx4AaABAagBo-6UCw"}, {"name": "Test Conversation"}]'
selector:
object:
message:
name: Message
description: List of message segments, only the "text" field is required in every segment.
required: true
example: '[{"text":"test", "is_bold": false, "is_italic": false, "is_strikethrough": false, "is_underline": false, "parse_str": false, "link_target": "http://google.com"}]'
selector:
object:
data:
name: Data
description: Other options ['image_file' / 'image_url']
example: '{ "image_file": "file" }'
selector:
object:
reconnect:
name: Reconnect
description: Reconnect the bot.

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"error": {
"invalid_login": "Invalid Login, please try again.",
"invalid_2fa": "Invalid 2 Factor Authentication, please try again.",
"invalid_2fa_method": "Invalid 2FA Method (verify on Phone)."
},
"step": {
"user": {
"data": {
"email": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]",
"authorization_code": "Authorization Code (required for manual authentication)"
},
"title": "Google Chat Login"
},
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"title": "2-Factor-Authentication"
}
}
}
}

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d",
"unknown": "\u0412\u044a\u0437\u043d\u0438\u043a\u043d\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430."
},
"error": {
"invalid_2fa": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u0430 2-\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f, \u043c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e.",
"invalid_2fa_method": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d \u043c\u0435\u0442\u043e\u0434 2FA (\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0430).",
"invalid_login": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0432\u043b\u0438\u0437\u0430\u043d\u0435, \u043c\u043e\u043b\u044f, \u043e\u043f\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u043d\u043e\u0432\u043e."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"title": "\u0414\u0432\u0443-\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0430\u0446\u0438\u044f"
},
"user": {
"data": {
"authorization_code": "\u041a\u043e\u0434 \u0437\u0430 \u043e\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f (\u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0437\u0430 \u0440\u044a\u0447\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0438\u0440\u0430\u043d\u0435)",
"email": "\u0418\u043c\u0435\u0439\u043b",
"password": "\u041f\u0430\u0440\u043e\u043b\u0430"
},
"title": "\u0412\u0445\u043e\u0434 \u0432 Google Hangouts"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "El servei ja est\u00e0 configurat",
"unknown": "Error inesperat"
},
"error": {
"invalid_2fa": "La verificaci\u00f3 en dos passos no \u00e9s v\u00e0lida, torna-ho a provar.",
"invalid_2fa_method": "M\u00e8tode 2FA inv\u00e0lid (verifica-ho al m\u00f2bil).",
"invalid_login": "L'inici de sessi\u00f3 no \u00e9s v\u00e0lid, torna-ho a provar."
},
"step": {
"2fa": {
"data": {
"2fa": "PIN 2FA"
},
"description": "Buit",
"title": "Verificaci\u00f3 en dos passos"
},
"user": {
"data": {
"authorization_code": "Codi d'autoritzaci\u00f3 (necessari per a l'autenticaci\u00f3 manual)",
"email": "Correu electr\u00f2nic",
"password": "Contrasenya"
},
"description": "Buit",
"title": "Inici de sessi\u00f3 de Google Chat"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Slu\u017eba je ji\u017e nastavena",
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
},
"error": {
"invalid_2fa": "Dfoufaktorov\u00e9 ov\u011b\u0159en\u00ed se nezda\u0159ilo. Zkuste to znovu.",
"invalid_2fa_method": "Neplatn\u00e1 metoda 2FA (ov\u011b\u0159en\u00ed na telefonu).",
"invalid_login": "Neplatn\u00e9 p\u0159ihla\u0161ovac\u00ed jm\u00e9no, pros\u00edm zkuste to znovu."
},
"step": {
"2fa": {
"data": {
"2fa": "Dvoufaktorov\u00fd ov\u011b\u0159ovac\u00ed k\u00f3d"
},
"description": "Pr\u00e1zdn\u00e9",
"title": "Dvoufaktorov\u00e9 ov\u011b\u0159en\u00ed"
},
"user": {
"data": {
"authorization_code": "Autoriza\u010dn\u00ed k\u00f3d (vy\u017eadov\u00e1n pro ru\u010dn\u00ed ov\u011b\u0159en\u00ed)",
"email": "E-mail",
"password": "Heslo"
},
"description": "Pr\u00e1zdn\u00e9",
"title": "P\u0159ihl\u00e1\u0161en\u00ed do slu\u017eby Google Hangouts"
}
}
}
}

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts er allerede konfigureret",
"unknown": "Ukendt fejl opstod"
},
"error": {
"invalid_2fa": "Ugyldig tofaktor-godkendelse, pr\u00f8v igen.",
"invalid_2fa_method": "Ugyldig 2FA-metode (Bekr\u00e6ft p\u00e5 telefon).",
"invalid_login": "Ugyldig login, pr\u00f8v venligst igen."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA pin"
},
"title": "Tofaktor-godkendelse"
},
"user": {
"data": {
"authorization_code": "Godkendelseskode (kr\u00e6vet til manuel godkendelse)",
"email": "Emailadresse",
"password": "Adgangskode"
},
"title": "Google Hangouts login"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Der Dienst ist bereits konfiguriert",
"unknown": "Unerwarteter Fehler"
},
"error": {
"invalid_2fa": "Ung\u00fcltige 2-Faktor Authentifizierung, bitte versuche es erneut.",
"invalid_2fa_method": "Ung\u00fcltige 2FA Methode (mit Telefon verifizieren)",
"invalid_login": "Ung\u00fcltiges Login, bitte versuche es erneut."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"description": "Leer",
"title": "2-Faktor-Authentifizierung"
},
"user": {
"data": {
"authorization_code": "Autorisierungscode (f\u00fcr die manuelle Authentifizierung erforderlich)",
"email": "E-Mail",
"password": "Passwort"
},
"description": "Leer",
"title": "Google Chat Anmeldung"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u0397 \u03c5\u03c0\u03b7\u03c1\u03b5\u03c3\u03af\u03b1 \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03c1\u03c5\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af",
"unknown": "\u0391\u03c0\u03c1\u03cc\u03c3\u03bc\u03b5\u03bd\u03bf \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1"
},
"error": {
"invalid_2fa": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03bf\u03c2 \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf\u03c2 \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac.",
"invalid_2fa_method": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03bc\u03ad\u03b8\u03bf\u03b4\u03bf\u03c2 2FA (\u03b5\u03c0\u03b1\u03bb\u03ae\u03b8\u03b5\u03c5\u03c3\u03b7 \u03c3\u03c4\u03bf \u03c4\u03b7\u03bb\u03ad\u03c6\u03c9\u03bd\u03bf).",
"invalid_login": "\u039c\u03b7 \u03ad\u03b3\u03ba\u03c5\u03c1\u03b7 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7, \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03be\u03b1\u03bd\u03ac."
},
"step": {
"2fa": {
"data": {
"2fa": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 PIN \u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7\u03c2 2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd"
},
"description": "\u039a\u03b5\u03bd\u03cc",
"title": "\u03a0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 2 \u03c0\u03b1\u03c1\u03b1\u03b3\u03cc\u03bd\u03c4\u03c9\u03bd"
},
"user": {
"data": {
"authorization_code": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03b5\u03be\u03bf\u03c5\u03c3\u03b9\u03bf\u03b4\u03cc\u03c4\u03b7\u03c3\u03b7\u03c2 (\u03b1\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b3\u03b9\u03b1 \u03c7\u03b5\u03b9\u03c1\u03bf\u03ba\u03af\u03bd\u03b7\u03c4\u03bf \u03ad\u03bb\u03b5\u03b3\u03c7\u03bf \u03c4\u03b1\u03c5\u03c4\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2)",
"email": "Email",
"password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2"
},
"description": "\u039a\u03b5\u03bd\u03cc",
"title": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 Google Hangouts"
}
}
}
}

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Service is already configured",
"unknown": "Unexpected error"
},
"error": {
"invalid_2fa": "Invalid 2 Factor Authentication, please try again.",
"invalid_2fa_method": "Invalid 2FA Method (verify on Phone).",
"invalid_login": "Invalid Login, please try again."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"title": "2-Factor-Authentication"
},
"user": {
"data": {
"authorization_code": "Authorization Code (required for manual authentication)",
"email": "Email",
"password": "Password"
},
"title": "Google Chat Login"
}
}
}
}

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts ya est\u00e1 configurado",
"unknown": "Se produjo un error desconocido."
},
"error": {
"invalid_2fa": "Autenticaci\u00f3n de 2 factores no v\u00e1lida, intente nuevamente.",
"invalid_2fa_method": "M\u00e9todo 2FA no v\u00e1lido (verificar en el tel\u00e9fono).",
"invalid_login": "Inicio de sesi\u00f3n no v\u00e1lido, por favor, int\u00e9ntalo de nuevo."
},
"step": {
"2fa": {
"data": {
"2fa": "Pin 2FA"
},
"title": "Autenticaci\u00f3n de 2 factores"
},
"user": {
"data": {
"authorization_code": "C\u00f3digo de autorizaci\u00f3n (requerido para la autenticaci\u00f3n manual)",
"email": "Direcci\u00f3n de correo electr\u00f3nico",
"password": "Contrase\u00f1a"
},
"title": "Inicio de sesi\u00f3n de Google Hangouts"
}
}
}
}

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"already_configured": "El servicio ya est\u00e1 configurado",
"unknown": "Error inesperado"
},
"error": {
"invalid_2fa": "Autenticaci\u00f3n de 2 factores no v\u00e1lida, por favor, int\u00e9ntalo de nuevo.",
"invalid_2fa_method": "M\u00e9todo 2FA no v\u00e1lido (verificar en el tel\u00e9fono).",
"invalid_login": "Inicio de sesi\u00f3n no v\u00e1lido, por favor, int\u00e9ntalo de nuevo."
},
"step": {
"2fa": {
"data": {
"2fa": "PIN 2FA"
},
"title": "Autenticaci\u00f3n de 2 factores"
},
"user": {
"data": {
"authorization_code": "C\u00f3digo de autorizaci\u00f3n (requerido para la autenticaci\u00f3n manual)",
"email": "Correo electr\u00f3nico",
"password": "Contrase\u00f1a"
},
"title": "Inicio de sesi\u00f3n de Google Chat"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Teenus on juba seadistatud",
"unknown": "Tundmatu viga"
},
"error": {
"invalid_2fa": "Vale 2-teguriline autentimine, proovi uuesti.",
"invalid_2fa_method": "Kehtetu kaheastmelise tuvastuse meetod (kontrolli telefonistl).",
"invalid_login": "Vale kasutajanimi, palun proovi uuesti."
},
"step": {
"2fa": {
"data": {
"2fa": "Kaheastmelise tuvastuse PIN"
},
"description": "",
"title": "Kaheastmeline autentimine"
},
"user": {
"data": {
"authorization_code": "Autoriseerimiskood (vajalik k\u00e4sitsi autentimiseks)",
"email": "E-posti aadress",
"password": "Salas\u00f5na"
},
"description": "",
"title": "Google Chat'i sisselogimine"
}
}
}
}

View File

@ -1,29 +0,0 @@
{
"config": {
"abort": {
"unknown": "Tapahtui tuntematon virhe."
},
"error": {
"invalid_2fa": "Virheellinen kaksitekij\u00e4todennus, yrit\u00e4 uudelleen.",
"invalid_2fa_method": "Virheellinen 2FA-menetelm\u00e4 (tarkista puhelimessa).",
"invalid_login": "Virheellinen kirjautuminen, yrit\u00e4 uudelleen."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA-pin"
},
"description": "Tyhj\u00e4",
"title": "Kaksivaiheinen tunnistus"
},
"user": {
"data": {
"email": "S\u00e4hk\u00f6postiosoite",
"password": "Salasana"
},
"description": "Tyhj\u00e4",
"title": "Google Hangouts -kirjautuminen"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9",
"unknown": "Erreur inattendue"
},
"error": {
"invalid_2fa": "Authentification \u00e0 deux facteurs non valide, veuillez r\u00e9essayer.",
"invalid_2fa_method": "M\u00e9thode 2FA non valide (v\u00e9rifiez sur le t\u00e9l\u00e9phone).",
"invalid_login": "Identifiant non valide, veuillez r\u00e9essayer."
},
"step": {
"2fa": {
"data": {
"2fa": "Code NIP d'authentification \u00e0 2 facteurs"
},
"description": "Vide",
"title": "Authentification \u00e0 2 facteurs"
},
"user": {
"data": {
"authorization_code": "Code d'autorisation (requis pour l'authentification manuelle)",
"email": "Courriel",
"password": "Mot de passe"
},
"description": "Vide",
"title": "Connexion \u00e0 Google Chat"
}
}
}
}

View File

@ -1,28 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u05e9\u05d9\u05e8\u05d5\u05ea \u05d6\u05d4 \u05db\u05d1\u05e8 \u05de\u05d5\u05d2\u05d3\u05e8",
"unknown": "\u05e9\u05d2\u05d9\u05d0\u05d4 \u05d1\u05dc\u05ea\u05d9 \u05e6\u05e4\u05d5\u05d9\u05d4"
},
"error": {
"invalid_2fa": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d1\u05d1\u05e7\u05e9\u05d4 \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1.",
"invalid_2fa_method": "\u05d3\u05e8\u05da \u05dc\u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea (\u05d0\u05de\u05ea \u05d1\u05d8\u05dc\u05e4\u05d5\u05df).",
"invalid_login": "\u05db\u05e0\u05d9\u05e1\u05d4 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9\u05ea, \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1."
},
"step": {
"2fa": {
"data": {
"2fa": "\u05e7\u05d5\u05d3 \u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9"
},
"title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d3\u05d5 \u05e9\u05dc\u05d1\u05d9"
},
"user": {
"data": {
"email": "\u05d3\u05d5\u05d0\"\u05dc",
"password": "\u05e1\u05d9\u05e1\u05de\u05d4"
},
"title": "\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05dc\u05e6'\u05d0\u05d8 \u05e9\u05dc \u05d2\u05d5\u05d2\u05dc"
}
}
}
}

View File

@ -1,12 +0,0 @@
{
"config": {
"step": {
"user": {
"data": {
"email": "Email",
"password": "Lozinka"
}
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "A szolg\u00e1ltat\u00e1s m\u00e1r konfigur\u00e1lva van",
"unknown": "V\u00e1ratlan hiba t\u00f6rt\u00e9nt"
},
"error": {
"invalid_2fa": "\u00c9rv\u00e9nytelen K\u00e9tfaktoros hiteles\u00edt\u00e9s, pr\u00f3b\u00e1ld \u00fajra.",
"invalid_2fa_method": "\u00c9rv\u00e9nytelen 2FA M\u00f3dszer (Ellen\u0151rz\u00e9s a Telefonon).",
"invalid_login": "\u00c9rv\u00e9nytelen bejelentkez\u00e9s, pr\u00f3b\u00e1ld \u00fajra."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"description": "\u00dcres",
"title": "K\u00e9tfaktoros Hiteles\u00edt\u00e9s"
},
"user": {
"data": {
"authorization_code": "Enged\u00e9lyez\u00e9si k\u00f3d (k\u00e9zi hiteles\u00edt\u00e9shez sz\u00fcks\u00e9ges)",
"email": "E-mail",
"password": "Jelsz\u00f3"
},
"description": "\u00dcres",
"title": "Google Chat bejelentkez\u00e9s"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Layanan sudah dikonfigurasi",
"unknown": "Kesalahan yang tidak diharapkan"
},
"error": {
"invalid_2fa": "Autentikasi 2 Faktor Tidak Valid, coba lagi.",
"invalid_2fa_method": "Metode 2FA Tidak Valid (Verifikasikan di Ponsel).",
"invalid_login": "Info Masuk Tidak Valid, coba lagi."
},
"step": {
"2fa": {
"data": {
"2fa": "PIN 2FA"
},
"description": "Kosong",
"title": "Autentikasi Dua Faktor"
},
"user": {
"data": {
"authorization_code": "Kode Otorisasi (diperlukan untuk autentikasi manual)",
"email": "Email",
"password": "Kata Sandi"
},
"description": "Kosong",
"title": "Info Masuk Google Chat"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Il servizio \u00e8 gi\u00e0 configurato",
"unknown": "Errore imprevisto"
},
"error": {
"invalid_2fa": "Autenticazione a 2 fattori non valida, riprova.",
"invalid_2fa_method": "Metodo 2FA non valido (verifica sul telefono).",
"invalid_login": "Accesso non valido, riprova."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"description": "Vuoto",
"title": "Autenticazione a due fattori"
},
"user": {
"data": {
"authorization_code": "Codice di autorizzazione (necessario per l'autenticazione manuale)",
"email": "Email",
"password": "Password"
},
"description": "Vuoto",
"title": "Accesso a Google Chat"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u30b5\u30fc\u30d3\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059",
"unknown": "\u4e88\u671f\u3057\u306a\u3044\u30a8\u30e9\u30fc"
},
"error": {
"invalid_2fa": "2\u8981\u7d20\u8a8d\u8a3c\u304c\u7121\u52b9\u3067\u3059\u3002\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002",
"invalid_2fa_method": "2\u8981\u7d20\u8a8d\u8a3c\u304c\u7121\u52b9(\u96fb\u8a71\u3067\u78ba\u8a8d)",
"invalid_login": "\u30ed\u30b0\u30a4\u30f3\u3067\u304d\u307e\u305b\u3093\u3001\u3082\u3046\u4e00\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002"
},
"step": {
"2fa": {
"data": {
"2fa": "2\u8981\u7d20 PIN"
},
"description": "\u7a7a",
"title": "2\u8981\u7d20\u8a8d\u8a3c"
},
"user": {
"data": {
"authorization_code": "\u8a8d\u8a3c\u30b3\u30fc\u30c9(\u624b\u52d5\u8a8d\u8a3c\u6642\u306b\u5fc5\u8981)",
"email": "E\u30e1\u30fc\u30eb",
"password": "\u30d1\u30b9\u30ef\u30fc\u30c9"
},
"description": "\u7a7a",
"title": "Google \u30cf\u30f3\u30b0\u30a2\u30a6\u30c8 \u30ed\u30b0\u30a4\u30f3"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\uc11c\ube44\uc2a4\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4",
"unknown": "\uc608\uc0c1\uce58 \ubabb\ud55c \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4"
},
"error": {
"invalid_2fa": "2\ub2e8\uacc4 \uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694.",
"invalid_2fa_method": "2\ub2e8\uacc4 \uc778\uc99d \ubc29\ubc95\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. (\uc804\ud654\uae30\uc5d0\uc11c \ud655\uc778)",
"invalid_login": "\ub85c\uadf8\uc778\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694."
},
"step": {
"2fa": {
"data": {
"2fa": "2\ub2e8\uacc4 \uc778\uc99d PIN"
},
"description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.",
"title": "2\ub2e8\uacc4 \uc778\uc99d"
},
"user": {
"data": {
"authorization_code": "\uc778\uc99d \ucf54\ub4dc (\uc218\ub3d9 \uc778\uc99d\uc5d0 \ud544\uc694)",
"email": "\uc774\uba54\uc77c",
"password": "\ube44\ubc00\ubc88\ud638"
},
"description": "\uc8c4\uc1a1\ud569\ub2c8\ub2e4. \uad00\ub828 \ub0b4\uc6a9\uc774 \uc544\uc9c1 \uc5c5\ub370\uc774\ud2b8 \ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \ucd94\ud6c4\uc5d0 \ubc18\uc601\ub420 \uc608\uc815\uc774\ub2c8 \uc870\uae08\ub9cc \uae30\ub2e4\ub824\uc8fc\uc138\uc694.",
"title": "Google \ud589\uc544\uc6c3 \ub85c\uadf8\uc778"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Service ass scho konfigur\u00e9iert",
"unknown": "Onerwaarte Feeler"
},
"error": {
"invalid_2fa": "Ong\u00eblteg 2-Faktor Authentifikatioun, prob\u00e9iert w.e.g. nach emol.",
"invalid_2fa_method": "Ong\u00eblteg 2FA Methode (Iwwerpr\u00e9ift et um Telefon)",
"invalid_login": "Ong\u00ebltege Login, prob\u00e9iert w.e.g. nach emol."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA Pin"
},
"description": "Eidel",
"title": "2-Faktor-Authentifikatioun"
},
"user": {
"data": {
"authorization_code": "Autorisatioun's Code (n\u00e9ideg fir eng manuell Authentifikatioun)",
"email": "E-Mail",
"password": "Passwuert"
},
"description": "Eidel",
"title": "Google Hangouts Login"
}
}
}
}

View File

@ -1,12 +0,0 @@
{
"config": {
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"title": "2 veiksni\u0173 autentifikavimas"
}
}
}
}

View File

@ -1,7 +0,0 @@
{
"config": {
"abort": {
"unknown": "Uventet feil"
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Dienst is al geconfigureerd",
"unknown": "Onverwachte fout"
},
"error": {
"invalid_2fa": "Ongeldige twee-factor-authenticatie, probeer het opnieuw.",
"invalid_2fa_method": "Ongeldige 2FA-methode (verifi\u00ebren op telefoon).",
"invalid_login": "Ongeldige aanmelding, probeer het opnieuw."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA pin"
},
"description": "Leeg",
"title": "Twee-factor-authenticatie"
},
"user": {
"data": {
"authorization_code": "Autorisatiecode (vereist voor handmatige authenticatie)",
"email": "E-mail",
"password": "Wachtwoord"
},
"description": "Leeg",
"title": "Google Chat-login"
}
}
}
}

View File

@ -1,28 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts er allereie konfigurert",
"unknown": "Det hende ein ukjent feil"
},
"error": {
"invalid_2fa": "Ugyldig to-faktor-autentisering. Ver vennleg og pr\u00f8v igjen.",
"invalid_2fa_method": "Ugyldig 2FA-metode (godkjenn p\u00e5 telefonen).",
"invalid_login": "Ugyldig innlogging. Pr\u00f8v igjen."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"title": "To-faktor-autentisering"
},
"user": {
"data": {
"email": "Epostadresse",
"password": "Passord"
},
"title": "Google Hangouts Login"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Tjenesten er allerede konfigurert",
"unknown": "Uventet feil"
},
"error": {
"invalid_2fa": "Ugyldig totrinnsbekreftelse, vennligst pr\u00f8v igjen.",
"invalid_2fa_method": "Ugyldig 2FA-metode (bekreft p\u00e5 telefon).",
"invalid_login": "Ugyldig innlogging, vennligst pr\u00f8v igjen."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN"
},
"description": "",
"title": "Totrinnsbekreftelse"
},
"user": {
"data": {
"authorization_code": "Godkjenningskode (kreves for manuell godkjenning)",
"email": "E-post",
"password": "Passord"
},
"description": "",
"title": "Google Chat-p\u00e5logging"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Us\u0142uga jest ju\u017c skonfigurowana",
"unknown": "Nieoczekiwany b\u0142\u0105d"
},
"error": {
"invalid_2fa": "Nieprawid\u0142owe uwierzytelnienie dwusk\u0142adnikowe, spr\u00f3buj ponownie",
"invalid_2fa_method": "Nieprawid\u0142owa metoda uwierzytelniania dwusk\u0142adnikowego (u\u017cyj weryfikacji przez telefon)",
"invalid_login": "Nieprawid\u0142owy login, spr\u00f3buj ponownie"
},
"step": {
"2fa": {
"data": {
"2fa": "Kod uwierzytelniania dwusk\u0142adnikowego"
},
"description": "Pusty",
"title": "Uwierzytelnianie dwusk\u0142adnikowe"
},
"user": {
"data": {
"authorization_code": "Kod autoryzacji (wymagany do r\u0119cznego uwierzytelnienia)",
"email": "Adres e-mail",
"password": "Has\u0142o"
},
"description": "Pusty",
"title": "Logowanie do Google Chat"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "O servi\u00e7o j\u00e1 est\u00e1 configurado",
"unknown": "Erro inesperado"
},
"error": {
"invalid_2fa": "Autentica\u00e7\u00e3o de 2 fatores inv\u00e1lida, por favor, tente novamente.",
"invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).",
"invalid_login": "Login inv\u00e1lido, por favor, tente novamente."
},
"step": {
"2fa": {
"data": {
"2fa": "C\u00f3digo 2FA"
},
"description": "Vazio",
"title": "Autentica\u00e7\u00e3o de 2 Fatores"
},
"user": {
"data": {
"authorization_code": "C\u00f3digo de Autoriza\u00e7\u00e3o (requerido para autentica\u00e7\u00e3o manual)",
"email": "Email",
"password": "Senha"
},
"description": "Vazio",
"title": "Login no Google Chat"
}
}
}
}

View File

@ -1,30 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts j\u00e1 est\u00e1 configurado",
"unknown": "Erro inesperado"
},
"error": {
"invalid_2fa": "Autentica\u00e7\u00e3o por 2 fatores inv\u00e1lida, por favor, tente novamente.",
"invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).",
"invalid_login": "Login inv\u00e1lido, por favor, tente novamente."
},
"step": {
"2fa": {
"data": {
"2fa": "Pin 2FA"
},
"description": "Vazio",
"title": "Autentica\u00e7\u00e3o de 2 Fatores"
},
"user": {
"data": {
"email": "E-mail",
"password": "Palavra-passe"
},
"description": "Vazio",
"title": "Login Google Hangouts"
}
}
}
}

View File

@ -1,27 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts este deja configurat",
"unknown": "Sa produs o eroare necunoscut\u0103."
},
"error": {
"invalid_2fa_method": "Metoda 2FA invalid\u0103 (Verifica\u021bi pe telefon).",
"invalid_login": "Conectare invalid\u0103, \u00eencerca\u021bi din nou."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA Pin"
}
},
"user": {
"data": {
"email": "Adresa de email",
"password": "Parol\u0103"
},
"description": "Gol",
"title": "Conectare Google Hangouts"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u042d\u0442\u0430 \u0441\u043b\u0443\u0436\u0431\u0430 \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.",
"unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."
},
"error": {
"invalid_2fa": "\u041d\u0435\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430.",
"invalid_2fa_method": "\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 (\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0435).",
"invalid_login": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u043e\u0432\u0430."
},
"step": {
"2fa": {
"data": {
"2fa": "\u041f\u0438\u043d-\u043a\u043e\u0434 \u0434\u043b\u044f \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438"
},
"description": "\u043f\u0443\u0441\u0442\u043e",
"title": "\u0414\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f"
},
"user": {
"data": {
"authorization_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 (\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438)",
"email": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b",
"password": "\u041f\u0430\u0440\u043e\u043b\u044c"
},
"description": "\u043f\u0443\u0441\u0442\u043e",
"title": "Google Chat"
}
}
}
}

View File

@ -1,12 +0,0 @@
{
"config": {
"step": {
"user": {
"data": {
"email": "Email",
"password": "Heslo"
}
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts je \u017ee konfiguriran",
"unknown": "Pri\u0161lo je do neznane napake"
},
"error": {
"invalid_2fa": "Neveljavna 2FA avtorizacija, prosimo, poskusite znova.",
"invalid_2fa_method": "Neveljavna 2FA Metoda (Preverite na Telefonu).",
"invalid_login": "Neveljavna Prijava, prosimo, poskusite znova."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA Pin"
},
"description": "prazno",
"title": "Dvofaktorska avtorizacija"
},
"user": {
"data": {
"authorization_code": "Koda pooblastila (potrebna za ro\u010dno overjanje)",
"email": "E-po\u0161tni naslov",
"password": "Geslo"
},
"description": "prazno",
"title": "Prijava za Google Hangouts"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts \u00e4r redan inst\u00e4llt",
"unknown": "Ett ok\u00e4nt fel intr\u00e4ffade"
},
"error": {
"invalid_2fa": "Ogiltig 2FA autentisering, f\u00f6rs\u00f6k igen.",
"invalid_2fa_method": "Ogiltig 2FA-metod (Verifiera med telefon).",
"invalid_login": "Ogiltig inloggning, f\u00f6rs\u00f6k igen."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA Pinkod"
},
"description": "Missing english translation",
"title": "Tv\u00e5faktorsautentisering"
},
"user": {
"data": {
"authorization_code": "Auktoriseringskod (kr\u00e4vs vid manuell verifiering)",
"email": "E-postadress",
"password": "L\u00f6senord"
},
"description": "Missing english translation",
"title": "Google Hangouts-inloggning"
}
}
}
}

View File

@ -1,16 +0,0 @@
{
"config": {
"step": {
"2fa": {
"title": "\u0e23\u0e2b\u0e31\u0e2a\u0e23\u0e31\u0e1a\u0e23\u0e2d\u0e07\u0e04\u0e27\u0e32\u0e21\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07\u0e2a\u0e2d\u0e07\u0e1b\u0e31\u0e08\u0e08\u0e31\u0e22"
},
"user": {
"data": {
"email": "\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48\u0e2d\u0e35\u0e40\u0e21\u0e25",
"password": "\u0e23\u0e2b\u0e31\u0e2a\u0e1c\u0e48\u0e32\u0e19"
},
"description": "\u0e27\u0e48\u0e32\u0e07\u0e40\u0e1b\u0e25\u0e48\u0e32"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Hizmet zaten yap\u0131land\u0131r\u0131lm\u0131\u015f",
"unknown": "Beklenmeyen hata"
},
"error": {
"invalid_2fa": "Ge\u00e7ersiz 2 Fakt\u00f6rl\u00fc Kimlik Do\u011frulama, l\u00fctfen tekrar deneyin.",
"invalid_2fa_method": "Ge\u00e7ersiz 2FA Y\u00f6ntemi (Telefonda do\u011frulay\u0131n).",
"invalid_login": "Ge\u00e7ersiz Giri\u015f, l\u00fctfen tekrar deneyin."
},
"step": {
"2fa": {
"data": {
"2fa": "2FA PIN'i"
},
"description": "Bo\u015f",
"title": "2-Fakt\u00f6rl\u00fc Kimlik Do\u011frulama"
},
"user": {
"data": {
"authorization_code": "Yetkilendirme Kodu (manuel kimlik do\u011frulama i\u00e7in gereklidir)",
"email": "E-posta",
"password": "Parola"
},
"description": "Bo\u015f",
"title": "Google Sohbet Giri\u015fi"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u0426\u044f \u0441\u043b\u0443\u0436\u0431\u0430 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u0430 \u0432 Home Assistant.",
"unknown": "\u041d\u0435\u043e\u0447\u0456\u043a\u0443\u0432\u0430\u043d\u0430 \u043f\u043e\u043c\u0438\u043b\u043a\u0430"
},
"error": {
"invalid_2fa": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u044f, \u0431\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0437\u043d\u043e\u0432\u0443.",
"invalid_2fa_method": "\u041d\u0435\u043f\u0440\u0438\u043f\u0443\u0441\u0442\u0438\u043c\u0438\u0439 \u0441\u043f\u043e\u0441\u0456\u0431 \u0434\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0457 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457 (\u043f\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043d\u0430 \u0442\u0435\u043b\u0435\u0444\u043e\u043d\u0456).",
"invalid_login": "\u041d\u0435\u0432\u0456\u0440\u043d\u0438\u0439 \u043b\u043e\u0433\u0456\u043d, \u0431\u0443\u0434\u044c \u043b\u0430\u0441\u043a\u0430, \u0441\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0437\u043d\u043e\u0432\u0443."
},
"step": {
"2fa": {
"data": {
"2fa": "\u041f\u0456\u043d-\u043a\u043e\u0434 \u0434\u043b\u044f \u0434\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0457 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457"
},
"description": "\u043f\u043e\u0440\u043e\u0436\u043d\u044c\u043e",
"title": "\u0414\u0432\u043e\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f"
},
"user": {
"data": {
"authorization_code": "\u041a\u043e\u0434 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0456\u0457 (\u0432\u0438\u043c\u0430\u0433\u0430\u0454\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0457 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u0457)",
"email": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438",
"password": "\u041f\u0430\u0440\u043e\u043b\u044c"
},
"description": "\u043f\u043e\u0440\u043e\u0436\u043d\u044c\u043e",
"title": "Google Hangouts"
}
}
}
}

View File

@ -1,7 +0,0 @@
{
"config": {
"error": {
"invalid_2fa_method": "Ph\u01b0\u01a1ng ph\u00e1p 2FA kh\u00f4ng h\u1ee3p l\u1ec7 (X\u00e1c minh tr\u00ean \u0111i\u1ec7n tho\u1ea1i)."
}
}
}

View File

@ -1,30 +0,0 @@
{
"config": {
"abort": {
"already_configured": "Google Hangouts \u5df2\u914d\u7f6e\u5b8c\u6210",
"unknown": "\u53d1\u751f\u672a\u77e5\u9519\u8bef\u3002"
},
"error": {
"invalid_2fa": "\u53cc\u91cd\u8ba4\u8bc1\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5\u3002",
"invalid_2fa_method": "\u65e0\u6548\u7684\u53cc\u91cd\u8ba4\u8bc1\u65b9\u6cd5\uff08\u7535\u8bdd\u9a8c\u8bc1\uff09\u3002",
"invalid_login": "\u767b\u9646\u5931\u8d25\uff0c\u8bf7\u518d\u8bd5\u4e00\u6b21\u3002"
},
"step": {
"2fa": {
"data": {
"2fa": "2FA Pin"
},
"description": "\u65e0",
"title": "\u53cc\u91cd\u8ba4\u8bc1"
},
"user": {
"data": {
"email": "\u7535\u5b50\u90ae\u4ef6\u5730\u5740",
"password": "\u5bc6\u7801"
},
"description": "\u65e0",
"title": "\u767b\u5f55 Google Hangouts"
}
}
}
}

View File

@ -1,31 +0,0 @@
{
"config": {
"abort": {
"already_configured": "\u670d\u52d9\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
"unknown": "\u672a\u9810\u671f\u932f\u8aa4"
},
"error": {
"invalid_2fa": "\u96d9\u91cd\u8a8d\u8b49\u7121\u6548\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002",
"invalid_2fa_method": "\u5169\u968e\u6bb5\u8a8d\u8b49\u65b9\u5f0f\u7121\u6548\uff08\u65bc\u96fb\u8a71\u4e0a\u9a57\u8b49\uff09\u3002",
"invalid_login": "\u767b\u5165\u5931\u6557\uff0c\u8acb\u518d\u8a66\u4e00\u6b21\u3002"
},
"step": {
"2fa": {
"data": {
"2fa": "\u5169\u968e\u6bb5\u8a8d\u8b49\u78bc"
},
"description": "\u7a7a\u767d",
"title": "\u96d9\u91cd\u8a8d\u8b49"
},
"user": {
"data": {
"authorization_code": "\u9a57\u8b49\u78bc\uff08\u624b\u52d5\u9a57\u8b49\u5fc5\u9808\uff09",
"email": "\u96fb\u5b50\u90f5\u4ef6",
"password": "\u5bc6\u78bc"
},
"description": "\u7a7a\u767d",
"title": "\u767b\u5165 Google Chat"
}
}
}
}

View File

@ -162,7 +162,6 @@ FLOWS = {
"growatt_server",
"guardian",
"habitica",
"hangouts",
"harmony",
"heos",
"here_travel_time",

View File

@ -2022,12 +2022,6 @@
"iot_class": "local_polling",
"name": "Google Cast"
},
"hangouts": {
"integration_type": "hub",
"config_flow": true,
"iot_class": "cloud_push",
"name": "Google Chat"
},
"dialogflow": {
"integration_type": "hub",
"config_flow": true,

View File

@ -847,9 +847,6 @@ ha-philipsjs==2.9.0
# homeassistant.components.habitica
habitipy==0.2.0
# homeassistant.components.hangouts
hangups==0.4.18
# homeassistant.components.cloud
hass-nabucasa==0.56.0

View File

@ -639,9 +639,6 @@ ha-philipsjs==2.9.0
# homeassistant.components.habitica
habitipy==0.2.0
# homeassistant.components.hangouts
hangups==0.4.18
# homeassistant.components.cloud
hass-nabucasa==0.56.0

View File

@ -1 +0,0 @@
"""Tests for the Hangouts Component."""

View File

@ -1,132 +0,0 @@
"""Tests for the Google Hangouts config flow."""
from unittest.mock import patch
from homeassistant import data_entry_flow
from homeassistant.components.hangouts import config_flow
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
EMAIL = "test@test.com"
PASSWORD = "1232456"
async def test_flow_works(hass, aioclient_mock):
"""Test config flow without 2fa."""
flow = config_flow.HangoutsFlowHandler()
flow.hass = hass
with patch("homeassistant.components.hangouts.config_flow.get_auth"):
result = await flow.async_step_user(
{CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD}
)
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
assert result["title"] == EMAIL
async def test_flow_works_with_authcode(hass, aioclient_mock):
"""Test config flow without 2fa."""
flow = config_flow.HangoutsFlowHandler()
flow.hass = hass
with patch("homeassistant.components.hangouts.config_flow.get_auth"):
result = await flow.async_step_user(
{
CONF_EMAIL: EMAIL,
CONF_PASSWORD: PASSWORD,
"authorization_code": "c29tZXJhbmRvbXN0cmluZw==",
}
)
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
assert result["title"] == EMAIL
async def test_flow_works_with_2fa(hass, aioclient_mock):
"""Test config flow with 2fa."""
from homeassistant.components.hangouts.hangups_utils import Google2FAError
flow = config_flow.HangoutsFlowHandler()
flow.hass = hass
with patch(
"homeassistant.components.hangouts.config_flow.get_auth",
side_effect=Google2FAError,
):
result = await flow.async_step_user(
{CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD}
)
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["step_id"] == "2fa"
with patch("homeassistant.components.hangouts.config_flow.get_auth"):
result = await flow.async_step_2fa({"2fa": 123456})
assert result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
assert result["title"] == EMAIL
async def test_flow_with_unknown_2fa(hass, aioclient_mock):
"""Test config flow with invalid 2fa method."""
from homeassistant.components.hangouts.hangups_utils import GoogleAuthError
flow = config_flow.HangoutsFlowHandler()
flow.hass = hass
with patch(
"homeassistant.components.hangouts.config_flow.get_auth",
side_effect=GoogleAuthError("Unknown verification code input"),
):
result = await flow.async_step_user(
{CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD}
)
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["errors"]["base"] == "invalid_2fa_method"
async def test_flow_invalid_login(hass, aioclient_mock):
"""Test config flow with invalid 2fa method."""
from homeassistant.components.hangouts.hangups_utils import GoogleAuthError
flow = config_flow.HangoutsFlowHandler()
flow.hass = hass
with patch(
"homeassistant.components.hangouts.config_flow.get_auth",
side_effect=GoogleAuthError,
):
result = await flow.async_step_user(
{CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD}
)
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["errors"]["base"] == "invalid_login"
async def test_flow_invalid_2fa(hass, aioclient_mock):
"""Test config flow with 2fa."""
from homeassistant.components.hangouts.hangups_utils import Google2FAError
flow = config_flow.HangoutsFlowHandler()
flow.hass = hass
with patch(
"homeassistant.components.hangouts.config_flow.get_auth",
side_effect=Google2FAError,
):
result = await flow.async_step_user(
{CONF_EMAIL: EMAIL, CONF_PASSWORD: PASSWORD}
)
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["step_id"] == "2fa"
with patch(
"homeassistant.components.hangouts.config_flow.get_auth",
side_effect=Google2FAError,
):
result = await flow.async_step_2fa({"2fa": 123456})
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["errors"]["base"] == "invalid_2fa"