Fix Tuya QR code expiry, use native QR selector (#109615)
* Fix Tuya QR code expiry, use native QR selector * Adjust testspull/109625/head
parent
e96f574a79
commit
b553bb71e6
|
@ -2,15 +2,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from io import BytesIO
|
||||
from typing import Any
|
||||
|
||||
import segno
|
||||
from tuya_sharing import LoginControl
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import selector
|
||||
|
||||
from .const import (
|
||||
CONF_ENDPOINT,
|
||||
|
@ -33,7 +32,6 @@ class TuyaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
|
||||
__user_code: str
|
||||
__qr_code: str
|
||||
__qr_image: str
|
||||
__reauth_entry: ConfigEntry | None = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
@ -82,9 +80,17 @@ class TuyaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="scan",
|
||||
description_placeholders={
|
||||
TUYA_RESPONSE_QR_CODE: self.__qr_image,
|
||||
},
|
||||
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(
|
||||
|
@ -94,11 +100,23 @@ class TuyaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
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_QR_CODE: self.__qr_image,
|
||||
TUYA_RESPONSE_MSG: info.get(TUYA_RESPONSE_MSG, "Unknown error"),
|
||||
TUYA_RESPONSE_CODE: info.get(TUYA_RESPONSE_CODE, 0),
|
||||
},
|
||||
|
@ -189,24 +207,4 @@ class TuyaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
if success := response.get(TUYA_RESPONSE_SUCCESS, False):
|
||||
self.__user_code = user_code
|
||||
self.__qr_code = response[TUYA_RESPONSE_RESULT][TUYA_RESPONSE_QR_CODE]
|
||||
self.__qr_image = _generate_qr_code(self.__qr_code)
|
||||
return success, response
|
||||
|
||||
|
||||
def _generate_qr_code(data: str) -> str:
|
||||
"""Create an SVG QR code that can be scanned with the Smart Life app."""
|
||||
qr_code = segno.make(f"tuyaSmart--qrLogin?token={data}", error="h")
|
||||
with BytesIO() as buffer:
|
||||
qr_code.save(
|
||||
buffer,
|
||||
kind="svg",
|
||||
border=5,
|
||||
scale=5,
|
||||
xmldecl=False,
|
||||
svgns=False,
|
||||
svgclass=None,
|
||||
lineclass=None,
|
||||
svgversion=2,
|
||||
dark="#1abcf2",
|
||||
)
|
||||
return str(buffer.getvalue().decode("ascii"))
|
||||
|
|
|
@ -43,5 +43,5 @@
|
|||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["tuya_iot"],
|
||||
"requirements": ["tuya-device-sharing-sdk==0.1.9", "segno==1.5.3"]
|
||||
"requirements": ["tuya-device-sharing-sdk==0.1.9"]
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
},
|
||||
"scan": {
|
||||
"description": "Use Smart Life app or Tuya Smart app to scan the following QR-code to complete the login:\n\n {qrcode} \n\nContinue to the next step once you have completed this step in the app."
|
||||
"description": "Use Smart Life app or Tuya Smart app to scan the following QR-code to complete the login.\n\nContinue to the next step once you have completed this step in the app."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
|
|
@ -2494,9 +2494,6 @@ scsgate==0.1.0
|
|||
# homeassistant.components.backup
|
||||
securetar==2023.3.0
|
||||
|
||||
# homeassistant.components.tuya
|
||||
segno==1.5.3
|
||||
|
||||
# homeassistant.components.sendgrid
|
||||
sendgrid==6.8.2
|
||||
|
||||
|
|
|
@ -1901,9 +1901,6 @@ screenlogicpy==0.10.0
|
|||
# homeassistant.components.backup
|
||||
securetar==2023.3.0
|
||||
|
||||
# homeassistant.components.tuya
|
||||
segno==1.5.3
|
||||
|
||||
# homeassistant.components.emulated_kasa
|
||||
# homeassistant.components.sense
|
||||
sense-energy==0.12.2
|
||||
|
|
|
@ -11,7 +11,7 @@ from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from tests.common import ANY, MockConfigEntry
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
|
@ -37,7 +37,6 @@ async def test_user_flow(
|
|||
|
||||
assert result2.get("type") == FlowResultType.FORM
|
||||
assert result2.get("step_id") == "scan"
|
||||
assert result2.get("description_placeholders") == {"qrcode": ANY}
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -157,7 +156,6 @@ async def test_reauth_flow(
|
|||
|
||||
assert result.get("type") == FlowResultType.FORM
|
||||
assert result.get("step_id") == "scan"
|
||||
assert result.get("description_placeholders") == {"qrcode": ANY}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -206,7 +204,6 @@ async def test_reauth_flow_migration(
|
|||
|
||||
assert result2.get("type") == FlowResultType.FORM
|
||||
assert result2.get("step_id") == "scan"
|
||||
assert result2.get("description_placeholders") == {"qrcode": ANY}
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
|
Loading…
Reference in New Issue