mirror of https://github.com/home-assistant/core
206 lines
6.6 KiB
Python
206 lines
6.6 KiB
Python
"""Config flow for Tuya."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
from typing import Any
|
|
|
|
from tuya_sharing import LoginControl
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
|
|
from homeassistant.helpers import selector
|
|
|
|
from .const import (
|
|
CONF_ENDPOINT,
|
|
CONF_TERMINAL_ID,
|
|
CONF_TOKEN_INFO,
|
|
CONF_USER_CODE,
|
|
DOMAIN,
|
|
TUYA_CLIENT_ID,
|
|
TUYA_RESPONSE_CODE,
|
|
TUYA_RESPONSE_MSG,
|
|
TUYA_RESPONSE_QR_CODE,
|
|
TUYA_RESPONSE_RESULT,
|
|
TUYA_RESPONSE_SUCCESS,
|
|
TUYA_SCHEMA,
|
|
)
|
|
|
|
|
|
class TuyaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
"""Tuya config flow."""
|
|
|
|
__user_code: str
|
|
__qr_code: str
|
|
|
|
def __init__(self) -> None:
|
|
"""Initialize the config flow."""
|
|
self.__login_control = LoginControl()
|
|
|
|
async def async_step_user(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Step user."""
|
|
errors = {}
|
|
placeholders = {}
|
|
|
|
if user_input is not None:
|
|
success, response = await self.__async_get_qr_code(
|
|
user_input[CONF_USER_CODE]
|
|
)
|
|
if success:
|
|
return await self.async_step_scan()
|
|
|
|
errors["base"] = "login_error"
|
|
placeholders = {
|
|
TUYA_RESPONSE_MSG: response.get(TUYA_RESPONSE_MSG, "Unknown error"),
|
|
TUYA_RESPONSE_CODE: response.get(TUYA_RESPONSE_CODE, "0"),
|
|
}
|
|
else:
|
|
user_input = {}
|
|
|
|
return self.async_show_form(
|
|
step_id="user",
|
|
data_schema=vol.Schema(
|
|
{
|
|
vol.Required(
|
|
CONF_USER_CODE, default=user_input.get(CONF_USER_CODE, "")
|
|
): str,
|
|
}
|
|
),
|
|
errors=errors,
|
|
description_placeholders=placeholders,
|
|
)
|
|
|
|
async def async_step_scan(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Step scan."""
|
|
if user_input is None:
|
|
return self.async_show_form(
|
|
step_id="scan",
|
|
data_schema=vol.Schema(
|
|
{
|
|
vol.Optional("QR"): selector.QrCodeSelector(
|
|
config=selector.QrCodeSelectorConfig(
|
|
data=f"tuyaSmart--qrLogin?token={self.__qr_code}",
|
|
scale=5,
|
|
error_correction_level=selector.QrErrorCorrectionLevel.QUARTILE,
|
|
)
|
|
)
|
|
}
|
|
),
|
|
)
|
|
|
|
ret, info = await self.hass.async_add_executor_job(
|
|
self.__login_control.login_result,
|
|
self.__qr_code,
|
|
TUYA_CLIENT_ID,
|
|
self.__user_code,
|
|
)
|
|
if not ret:
|
|
# Try to get a new QR code on failure
|
|
await self.__async_get_qr_code(self.__user_code)
|
|
return self.async_show_form(
|
|
step_id="scan",
|
|
errors={"base": "login_error"},
|
|
data_schema=vol.Schema(
|
|
{
|
|
vol.Optional("QR"): selector.QrCodeSelector(
|
|
config=selector.QrCodeSelectorConfig(
|
|
data=f"tuyaSmart--qrLogin?token={self.__qr_code}",
|
|
scale=5,
|
|
error_correction_level=selector.QrErrorCorrectionLevel.QUARTILE,
|
|
)
|
|
)
|
|
}
|
|
),
|
|
description_placeholders={
|
|
TUYA_RESPONSE_MSG: info.get(TUYA_RESPONSE_MSG, "Unknown error"),
|
|
TUYA_RESPONSE_CODE: info.get(TUYA_RESPONSE_CODE, 0),
|
|
},
|
|
)
|
|
|
|
entry_data = {
|
|
CONF_USER_CODE: self.__user_code,
|
|
CONF_TOKEN_INFO: {
|
|
"t": info["t"],
|
|
"uid": info["uid"],
|
|
"expire_time": info["expire_time"],
|
|
"access_token": info["access_token"],
|
|
"refresh_token": info["refresh_token"],
|
|
},
|
|
CONF_TERMINAL_ID: info[CONF_TERMINAL_ID],
|
|
CONF_ENDPOINT: info[CONF_ENDPOINT],
|
|
}
|
|
|
|
if self.source == SOURCE_REAUTH:
|
|
return self.async_update_reload_and_abort(
|
|
self._get_reauth_entry(),
|
|
data=entry_data,
|
|
)
|
|
|
|
return self.async_create_entry(
|
|
title=info.get("username"),
|
|
data=entry_data,
|
|
)
|
|
|
|
async def async_step_reauth(
|
|
self, entry_data: Mapping[str, Any]
|
|
) -> ConfigFlowResult:
|
|
"""Handle initiation of re-authentication with Tuya."""
|
|
if CONF_USER_CODE in entry_data:
|
|
success, _ = await self.__async_get_qr_code(entry_data[CONF_USER_CODE])
|
|
if success:
|
|
return await self.async_step_scan()
|
|
|
|
return await self.async_step_reauth_user_code()
|
|
|
|
async def async_step_reauth_user_code(
|
|
self, user_input: dict[str, Any] | None = None
|
|
) -> ConfigFlowResult:
|
|
"""Handle re-authentication with a Tuya."""
|
|
errors = {}
|
|
placeholders = {}
|
|
|
|
if user_input is not None:
|
|
success, response = await self.__async_get_qr_code(
|
|
user_input[CONF_USER_CODE]
|
|
)
|
|
if success:
|
|
return await self.async_step_scan()
|
|
|
|
errors["base"] = "login_error"
|
|
placeholders = {
|
|
TUYA_RESPONSE_MSG: response.get(TUYA_RESPONSE_MSG, "Unknown error"),
|
|
TUYA_RESPONSE_CODE: response.get(TUYA_RESPONSE_CODE, "0"),
|
|
}
|
|
else:
|
|
user_input = {}
|
|
|
|
return self.async_show_form(
|
|
step_id="reauth_user_code",
|
|
data_schema=vol.Schema(
|
|
{
|
|
vol.Required(
|
|
CONF_USER_CODE, default=user_input.get(CONF_USER_CODE, "")
|
|
): str,
|
|
}
|
|
),
|
|
errors=errors,
|
|
description_placeholders=placeholders,
|
|
)
|
|
|
|
async def __async_get_qr_code(self, user_code: str) -> tuple[bool, dict[str, Any]]:
|
|
"""Get the QR code."""
|
|
response = await self.hass.async_add_executor_job(
|
|
self.__login_control.qr_code,
|
|
TUYA_CLIENT_ID,
|
|
TUYA_SCHEMA,
|
|
user_code,
|
|
)
|
|
if success := response.get(TUYA_RESPONSE_SUCCESS, False):
|
|
self.__user_code = user_code
|
|
self.__qr_code = response[TUYA_RESPONSE_RESULT][TUYA_RESPONSE_QR_CODE]
|
|
return success, response
|