mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-01-24 13:47:05 +00:00
Allow https for klaptransport (#1415)
Some checks failed
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Has been cancelled
Some checks failed
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Has been cancelled
Later firmware versions on robovacs use `KLAP` over https instead of ssltransport (reported as AES)
This commit is contained in:
parent
fa0f7157c6
commit
7b1b14d1e6
@ -204,7 +204,7 @@ The following devices have been tested and confirmed as working. If your device
|
||||
- **Cameras**: C100, C210, C220, C225, C325WB, C520WS, C720, D230, TC65, TC70
|
||||
- **Hubs**: H100, H200
|
||||
- **Hub-Connected Devices[^3]**: S200B, S200D, T100, T110, T300, T310, T315
|
||||
- **Vacuums**: RV20 Max Plus
|
||||
- **Vacuums**: RV20 Max Plus, RV30 Max
|
||||
|
||||
<!--SUPPORTED_END-->
|
||||
[^1]: Model requires authentication
|
||||
|
@ -330,6 +330,8 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
|
||||
|
||||
- **RV20 Max Plus**
|
||||
- Hardware: 1.0 (EU) / Firmware: 1.0.7
|
||||
- **RV30 Max**
|
||||
- Hardware: 1.0 (US) / Firmware: 1.2.0
|
||||
|
||||
|
||||
<!--SUPPORTED_END-->
|
||||
|
@ -300,7 +300,9 @@ async def cli(
|
||||
connection_type = DeviceConnectionParameters.from_values(
|
||||
dr.device_type,
|
||||
dr.mgt_encrypt_schm.encrypt_type,
|
||||
dr.mgt_encrypt_schm.lv,
|
||||
login_version=dr.mgt_encrypt_schm.lv,
|
||||
https=dr.mgt_encrypt_schm.is_support_https,
|
||||
http_port=dr.mgt_encrypt_schm.http_port,
|
||||
)
|
||||
dc = DeviceConfig(
|
||||
host=host,
|
||||
|
@ -261,8 +261,11 @@ async def config(ctx: click.Context) -> DeviceDict:
|
||||
host_port = host + (f":{port}" if port else "")
|
||||
|
||||
def on_attempt(connect_attempt: ConnectAttempt, success: bool) -> None:
|
||||
prot, tran, dev = connect_attempt
|
||||
key_str = f"{prot.__name__} + {tran.__name__} + {dev.__name__}"
|
||||
prot, tran, dev, https = connect_attempt
|
||||
key_str = (
|
||||
f"{prot.__name__} + {tran.__name__} + {dev.__name__}"
|
||||
f" + {'https' if https else 'http'}"
|
||||
)
|
||||
result = "succeeded" if success else "failed"
|
||||
msg = f"Attempt to connect to {host_port} with {key_str} {result}"
|
||||
echo(msg)
|
||||
|
@ -189,6 +189,7 @@ def get_protocol(config: DeviceConfig, *, strict: bool = False) -> BaseProtocol
|
||||
:param config: Device config to derive protocol
|
||||
:param strict: Require exact match on encrypt type
|
||||
"""
|
||||
_LOGGER.debug("Finding protocol for %s", config.host)
|
||||
ctype = config.connection_type
|
||||
protocol_name = ctype.device_family.value.split(".")[0]
|
||||
_LOGGER.debug("Finding protocol for %s", ctype.device_family)
|
||||
@ -203,9 +204,11 @@ def get_protocol(config: DeviceConfig, *, strict: bool = False) -> BaseProtocol
|
||||
return None
|
||||
return IotProtocol(transport=LinkieTransportV2(config=config))
|
||||
|
||||
if ctype.device_family is DeviceFamily.SmartTapoRobovac:
|
||||
if strict and ctype.encryption_type is not DeviceEncryptionType.Aes:
|
||||
return None
|
||||
# Older FW used a different transport
|
||||
if (
|
||||
ctype.device_family is DeviceFamily.SmartTapoRobovac
|
||||
and ctype.encryption_type is DeviceEncryptionType.Aes
|
||||
):
|
||||
return SmartProtocol(transport=SslTransport(config=config))
|
||||
|
||||
protocol_transport_key = (
|
||||
@ -223,6 +226,7 @@ def get_protocol(config: DeviceConfig, *, strict: bool = False) -> BaseProtocol
|
||||
"IOT.KLAP": (IotProtocol, KlapTransport),
|
||||
"SMART.AES": (SmartProtocol, AesTransport),
|
||||
"SMART.KLAP": (SmartProtocol, KlapTransportV2),
|
||||
"SMART.KLAP.HTTPS": (SmartProtocol, KlapTransportV2),
|
||||
# H200 is device family SMART.TAPOHUB and uses SmartCamProtocol so use
|
||||
# https to distuingish from SmartProtocol devices
|
||||
"SMART.AES.HTTPS": (SmartCamProtocol, SslAesTransport),
|
||||
|
@ -20,7 +20,7 @@ None
|
||||
{'host': '127.0.0.3', 'timeout': 5, 'credentials': {'username': 'user@example.com', \
|
||||
'password': 'great_password'}, 'connection_type'\
|
||||
: {'device_family': 'SMART.TAPOBULB', 'encryption_type': 'KLAP', 'login_version': 2, \
|
||||
'https': False}}
|
||||
'https': False, 'http_port': 80}}
|
||||
|
||||
>>> later_device = await Device.connect(config=Device.Config.from_dict(config_dict))
|
||||
>>> print(later_device.alias) # Alias is available as connect() calls update()
|
||||
@ -98,13 +98,16 @@ class DeviceConnectionParameters(_DeviceConfigBaseMixin):
|
||||
encryption_type: DeviceEncryptionType
|
||||
login_version: int | None = None
|
||||
https: bool = False
|
||||
http_port: int | None = None
|
||||
|
||||
@staticmethod
|
||||
def from_values(
|
||||
device_family: str,
|
||||
encryption_type: str,
|
||||
*,
|
||||
login_version: int | None = None,
|
||||
https: bool | None = None,
|
||||
http_port: int | None = None,
|
||||
) -> DeviceConnectionParameters:
|
||||
"""Return connection parameters from string values."""
|
||||
try:
|
||||
@ -115,6 +118,7 @@ class DeviceConnectionParameters(_DeviceConfigBaseMixin):
|
||||
DeviceEncryptionType(encryption_type),
|
||||
login_version,
|
||||
https,
|
||||
http_port=http_port,
|
||||
)
|
||||
except (ValueError, TypeError) as ex:
|
||||
raise KasaException(
|
||||
|
@ -146,6 +146,7 @@ class ConnectAttempt(NamedTuple):
|
||||
protocol: type
|
||||
transport: type
|
||||
device: type
|
||||
https: bool
|
||||
|
||||
|
||||
class DiscoveredMeta(TypedDict):
|
||||
@ -637,10 +638,10 @@ class Discover:
|
||||
Device.Family.IotIpCamera,
|
||||
}
|
||||
candidates: dict[
|
||||
tuple[type[BaseProtocol], type[BaseTransport], type[Device]],
|
||||
tuple[type[BaseProtocol], type[BaseTransport], type[Device], bool],
|
||||
tuple[BaseProtocol, DeviceConfig],
|
||||
] = {
|
||||
(type(protocol), type(protocol._transport), device_class): (
|
||||
(type(protocol), type(protocol._transport), device_class, https): (
|
||||
protocol,
|
||||
config,
|
||||
)
|
||||
@ -870,8 +871,9 @@ class Discover:
|
||||
config.connection_type = DeviceConnectionParameters.from_values(
|
||||
type_,
|
||||
encrypt_type,
|
||||
login_version,
|
||||
encrypt_schm.is_support_https,
|
||||
login_version=login_version,
|
||||
https=encrypt_schm.is_support_https,
|
||||
http_port=encrypt_schm.http_port,
|
||||
)
|
||||
except KasaException as ex:
|
||||
raise UnsupportedDeviceError(
|
||||
|
@ -36,6 +36,18 @@ if TYPE_CHECKING:
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _mask_area_list(area_list: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
def mask_area(area: dict[str, Any]) -> dict[str, Any]:
|
||||
result = {**area}
|
||||
# Will leave empty names as blank
|
||||
if area.get("name"):
|
||||
result["name"] = "I01BU0tFRF9OQU1FIw==" # #MASKED_NAME#
|
||||
return result
|
||||
|
||||
return [mask_area(area) for area in area_list]
|
||||
|
||||
|
||||
REDACTORS: dict[str, Callable[[Any], Any] | None] = {
|
||||
"latitude": lambda x: 0,
|
||||
"longitude": lambda x: 0,
|
||||
@ -71,6 +83,10 @@ REDACTORS: dict[str, Callable[[Any], Any] | None] = {
|
||||
"custom_sn": lambda _: "000000000000",
|
||||
"location": lambda x: "#MASKED_NAME#" if x else "",
|
||||
"map_data": lambda x: "#SCRUBBED_MAPDATA#" if x else "",
|
||||
"map_name": lambda x: "I01BU0tFRF9OQU1FIw==", # #MASKED_NAME#
|
||||
"area_list": _mask_area_list,
|
||||
# unknown robovac binary blob in get_device_info
|
||||
"cd": lambda x: "I01BU0tFRF9CSU5BUlkj", # #MASKED_BINARY#
|
||||
}
|
||||
|
||||
# Queries that are known not to work properly when sent as a
|
||||
|
@ -120,6 +120,8 @@ class AesTransport(BaseTransport):
|
||||
@property
|
||||
def default_port(self) -> int:
|
||||
"""Default port for the transport."""
|
||||
if port := self._config.connection_type.http_port:
|
||||
return port
|
||||
return self.DEFAULT_PORT
|
||||
|
||||
@property
|
||||
|
@ -48,6 +48,7 @@ import datetime
|
||||
import hashlib
|
||||
import logging
|
||||
import secrets
|
||||
import ssl
|
||||
import struct
|
||||
import time
|
||||
from asyncio import Future
|
||||
@ -92,8 +93,21 @@ class KlapTransport(BaseTransport):
|
||||
"""
|
||||
|
||||
DEFAULT_PORT: int = 80
|
||||
DEFAULT_HTTPS_PORT: int = 4433
|
||||
|
||||
SESSION_COOKIE_NAME = "TP_SESSIONID"
|
||||
TIMEOUT_COOKIE_NAME = "TIMEOUT"
|
||||
# Copy & paste from sslaestransport
|
||||
CIPHERS = ":".join(
|
||||
[
|
||||
"AES256-GCM-SHA384",
|
||||
"AES256-SHA256",
|
||||
"AES128-GCM-SHA256",
|
||||
"AES128-SHA256",
|
||||
"AES256-SHA",
|
||||
]
|
||||
)
|
||||
_ssl_context: ssl.SSLContext | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -125,12 +139,20 @@ class KlapTransport(BaseTransport):
|
||||
self._session_cookie: dict[str, Any] | None = None
|
||||
|
||||
_LOGGER.debug("Created KLAP transport for %s", self._host)
|
||||
self._app_url = URL(f"http://{self._host}:{self._port}/app")
|
||||
protocol = "https" if config.connection_type.https else "http"
|
||||
self._app_url = URL(f"{protocol}://{self._host}:{self._port}/app")
|
||||
self._request_url = self._app_url / "request"
|
||||
|
||||
@property
|
||||
def default_port(self) -> int:
|
||||
"""Default port for the transport."""
|
||||
config = self._config
|
||||
if port := config.connection_type.http_port:
|
||||
return port
|
||||
|
||||
if config.connection_type.https:
|
||||
return self.DEFAULT_HTTPS_PORT
|
||||
|
||||
return self.DEFAULT_PORT
|
||||
|
||||
@property
|
||||
@ -152,7 +174,9 @@ class KlapTransport(BaseTransport):
|
||||
|
||||
url = self._app_url / "handshake1"
|
||||
|
||||
response_status, response_data = await self._http_client.post(url, data=payload)
|
||||
response_status, response_data = await self._http_client.post(
|
||||
url, data=payload, ssl=await self._get_ssl_context()
|
||||
)
|
||||
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
_LOGGER.debug(
|
||||
@ -263,6 +287,7 @@ class KlapTransport(BaseTransport):
|
||||
url,
|
||||
data=payload,
|
||||
cookies_dict=self._session_cookie,
|
||||
ssl=await self._get_ssl_context(),
|
||||
)
|
||||
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
@ -337,6 +362,7 @@ class KlapTransport(BaseTransport):
|
||||
params={"seq": seq},
|
||||
data=payload,
|
||||
cookies_dict=self._session_cookie,
|
||||
ssl=await self._get_ssl_context(),
|
||||
)
|
||||
|
||||
msg = (
|
||||
@ -413,6 +439,23 @@ class KlapTransport(BaseTransport):
|
||||
un = creds.username
|
||||
return md5(un.encode())
|
||||
|
||||
# Copy & paste from sslaestransport.
|
||||
def _create_ssl_context(self) -> ssl.SSLContext:
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
context.set_ciphers(self.CIPHERS)
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
return context
|
||||
|
||||
# Copy & paste from sslaestransport.
|
||||
async def _get_ssl_context(self) -> ssl.SSLContext:
|
||||
if not self._ssl_context:
|
||||
loop = asyncio.get_running_loop()
|
||||
self._ssl_context = await loop.run_in_executor(
|
||||
None, self._create_ssl_context
|
||||
)
|
||||
return self._ssl_context
|
||||
|
||||
|
||||
class KlapTransportV2(KlapTransport):
|
||||
"""Implementation of the KLAP encryption protocol with v2 hanshake hashes."""
|
||||
|
@ -55,6 +55,8 @@ class LinkieTransportV2(BaseTransport):
|
||||
@property
|
||||
def default_port(self) -> int:
|
||||
"""Default port for the transport."""
|
||||
if port := self._config.connection_type.http_port:
|
||||
return port
|
||||
return self.DEFAULT_PORT
|
||||
|
||||
@property
|
||||
|
@ -133,6 +133,8 @@ class SslAesTransport(BaseTransport):
|
||||
@property
|
||||
def default_port(self) -> int:
|
||||
"""Default port for the transport."""
|
||||
if port := self._config.connection_type.http_port:
|
||||
return port
|
||||
return self.DEFAULT_PORT
|
||||
|
||||
@staticmethod
|
||||
|
@ -94,6 +94,8 @@ class SslTransport(BaseTransport):
|
||||
@property
|
||||
def default_port(self) -> int:
|
||||
"""Default port for the transport."""
|
||||
if port := self._config.connection_type.http_port:
|
||||
return port
|
||||
return self.DEFAULT_PORT
|
||||
|
||||
@property
|
||||
|
@ -159,6 +159,7 @@ def create_discovery_mock(ip: str, fixture_data: dict):
|
||||
https: bool
|
||||
login_version: int | None = None
|
||||
port_override: int | None = None
|
||||
http_port: int | None = None
|
||||
|
||||
@property
|
||||
def model(self) -> str:
|
||||
@ -194,9 +195,15 @@ def create_discovery_mock(ip: str, fixture_data: dict):
|
||||
):
|
||||
login_version = max([int(i) for i in et])
|
||||
https = discovery_result["mgt_encrypt_schm"]["is_support_https"]
|
||||
http_port = discovery_result["mgt_encrypt_schm"].get("http_port")
|
||||
if not http_port: # noqa: SIM108
|
||||
# Not all discovery responses set the http port, i.e. smartcam.
|
||||
default_port = 443 if https else 80
|
||||
else:
|
||||
default_port = http_port
|
||||
dm = _DiscoveryMock(
|
||||
ip,
|
||||
80,
|
||||
default_port,
|
||||
20002,
|
||||
discovery_data,
|
||||
fixture_data,
|
||||
@ -204,6 +211,7 @@ def create_discovery_mock(ip: str, fixture_data: dict):
|
||||
encrypt_type,
|
||||
https,
|
||||
login_version,
|
||||
http_port=http_port,
|
||||
)
|
||||
else:
|
||||
sys_info = fixture_data["system"]["get_sysinfo"]
|
||||
|
888
tests/fixtures/smart/RV30 Max(US)_1.0_1.2.0.json
vendored
Normal file
888
tests/fixtures/smart/RV30 Max(US)_1.0_1.2.0.json
vendored
Normal file
@ -0,0 +1,888 @@
|
||||
{
|
||||
"component_nego": {
|
||||
"component_list": [
|
||||
{
|
||||
"id": "device",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "iot_cloud",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "time",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "firmware",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "quick_setup",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "clean",
|
||||
"ver_code": 3
|
||||
},
|
||||
{
|
||||
"id": "battery",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "consumables",
|
||||
"ver_code": 2
|
||||
},
|
||||
{
|
||||
"id": "direction_control",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "button_and_led",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "speaker",
|
||||
"ver_code": 3
|
||||
},
|
||||
{
|
||||
"id": "schedule",
|
||||
"ver_code": 3
|
||||
},
|
||||
{
|
||||
"id": "wireless",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "map",
|
||||
"ver_code": 2
|
||||
},
|
||||
{
|
||||
"id": "auto_change_map",
|
||||
"ver_code": 2
|
||||
},
|
||||
{
|
||||
"id": "mop",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "ble_whole_setup",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "do_not_disturb",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "inherit",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "device_local_time",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "charge_pose_clean",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "continue_breakpoint_sweep",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "goto_point",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "furniture",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "map_cloud_backup",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "dev_log",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "map_lock",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "carpet_area",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "clean_angle",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "clean_percent",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "no_pose_config",
|
||||
"ver_code": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"discovery_result": {
|
||||
"error_code": 0,
|
||||
"result": {
|
||||
"device_id": "00000000000000000000000000000000",
|
||||
"device_model": "RV30 Max(US)",
|
||||
"device_type": "SMART.TAPOROBOVAC",
|
||||
"factory_default": false,
|
||||
"ip": "127.0.0.123",
|
||||
"is_support_iot_cloud": true,
|
||||
"mac": "7C-F1-7E-00-00-00",
|
||||
"mgt_encrypt_schm": {
|
||||
"encrypt_type": "KLAP",
|
||||
"http_port": 4433,
|
||||
"is_support_https": true
|
||||
},
|
||||
"obd_src": "tplink",
|
||||
"owner": "00000000000000000000000000000000",
|
||||
"protocol_version": 1
|
||||
}
|
||||
},
|
||||
"getAreaUnit": {
|
||||
"area_unit": 1
|
||||
},
|
||||
"getAutoChangeMap": {
|
||||
"auto_change_map": true
|
||||
},
|
||||
"getBatteryInfo": {
|
||||
"battery_percentage": 100
|
||||
},
|
||||
"getCarpetClean": {
|
||||
"carpet_clean_prefer": "boost"
|
||||
},
|
||||
"getChildLockInfo": {
|
||||
"child_lock_status": false
|
||||
},
|
||||
"getCleanAttr": {
|
||||
"cistern": 1,
|
||||
"clean_number": 1,
|
||||
"suction": 2
|
||||
},
|
||||
"getCleanInfo": {
|
||||
"clean_area": 59,
|
||||
"clean_percent": 100,
|
||||
"clean_time": 56
|
||||
},
|
||||
"getCleanRecords": {
|
||||
"lastest_day_record": [
|
||||
1737387294,
|
||||
56,
|
||||
59,
|
||||
1
|
||||
],
|
||||
"record_list": [
|
||||
{
|
||||
"clean_area": 59,
|
||||
"clean_time": 57,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 0,
|
||||
"start_type": 4,
|
||||
"task_type": 0,
|
||||
"timestamp": 1737041654
|
||||
},
|
||||
{
|
||||
"clean_area": 39,
|
||||
"clean_time": 58,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 1,
|
||||
"map_id": 1736541042,
|
||||
"message": 0,
|
||||
"record_index": 1,
|
||||
"start_type": 1,
|
||||
"task_type": 0,
|
||||
"timestamp": 1737055944
|
||||
},
|
||||
{
|
||||
"clean_area": 1,
|
||||
"clean_time": 3,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 2,
|
||||
"start_type": 1,
|
||||
"task_type": 4,
|
||||
"timestamp": 1737074472
|
||||
},
|
||||
{
|
||||
"clean_area": 59,
|
||||
"clean_time": 58,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 3,
|
||||
"start_type": 4,
|
||||
"task_type": 0,
|
||||
"timestamp": 1737128195
|
||||
},
|
||||
{
|
||||
"clean_area": 68,
|
||||
"clean_time": 78,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 2,
|
||||
"map_id": 1736541042,
|
||||
"message": 0,
|
||||
"record_index": 4,
|
||||
"start_type": 1,
|
||||
"task_type": 1,
|
||||
"timestamp": 1737216716
|
||||
},
|
||||
{
|
||||
"clean_area": 3,
|
||||
"clean_time": 3,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734742958,
|
||||
"message": 0,
|
||||
"record_index": 5,
|
||||
"start_type": 1,
|
||||
"task_type": 3,
|
||||
"timestamp": 1737300731
|
||||
},
|
||||
{
|
||||
"clean_area": 20,
|
||||
"clean_time": 16,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734742958,
|
||||
"message": 0,
|
||||
"record_index": 6,
|
||||
"start_type": 1,
|
||||
"task_type": 3,
|
||||
"timestamp": 1737304391
|
||||
},
|
||||
{
|
||||
"clean_area": 59,
|
||||
"clean_time": 56,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 7,
|
||||
"start_type": 4,
|
||||
"task_type": 0,
|
||||
"timestamp": 1737387294
|
||||
},
|
||||
{
|
||||
"clean_area": 17,
|
||||
"clean_time": 16,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 8,
|
||||
"start_type": 1,
|
||||
"task_type": 3,
|
||||
"timestamp": 1736707487
|
||||
},
|
||||
{
|
||||
"clean_area": 8,
|
||||
"clean_time": 10,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 9,
|
||||
"start_type": 1,
|
||||
"task_type": 4,
|
||||
"timestamp": 1736708425
|
||||
},
|
||||
{
|
||||
"clean_area": 59,
|
||||
"clean_time": 54,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 10,
|
||||
"start_type": 4,
|
||||
"task_type": 0,
|
||||
"timestamp": 1736782261
|
||||
},
|
||||
{
|
||||
"clean_area": 60,
|
||||
"clean_time": 56,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 11,
|
||||
"start_type": 4,
|
||||
"task_type": 0,
|
||||
"timestamp": 1736868752
|
||||
},
|
||||
{
|
||||
"clean_area": 58,
|
||||
"clean_time": 68,
|
||||
"dust_collection": true,
|
||||
"error": 1,
|
||||
"info_num": 0,
|
||||
"map_id": 1736541042,
|
||||
"message": 0,
|
||||
"record_index": 12,
|
||||
"start_type": 1,
|
||||
"task_type": 1,
|
||||
"timestamp": 1736881428
|
||||
},
|
||||
{
|
||||
"clean_area": 59,
|
||||
"clean_time": 59,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 13,
|
||||
"start_type": 4,
|
||||
"task_type": 0,
|
||||
"timestamp": 1736955682
|
||||
},
|
||||
{
|
||||
"clean_area": 36,
|
||||
"clean_time": 33,
|
||||
"dust_collection": false,
|
||||
"error": 0,
|
||||
"info_num": 0,
|
||||
"map_id": 1734727686,
|
||||
"message": 0,
|
||||
"record_index": 14,
|
||||
"start_type": 1,
|
||||
"task_type": 4,
|
||||
"timestamp": 1736960713
|
||||
}
|
||||
],
|
||||
"record_list_num": 15,
|
||||
"total_area": 2304,
|
||||
"total_number": 85,
|
||||
"total_time": 2510
|
||||
},
|
||||
"getCleanStatus": {
|
||||
"clean_status": 0,
|
||||
"is_mapping": false,
|
||||
"is_relocating": false,
|
||||
"is_working": false
|
||||
},
|
||||
"getConsumablesInfo": {
|
||||
"charge_contact_time": 660,
|
||||
"edge_brush_time": 2743,
|
||||
"filter_time": 287,
|
||||
"main_brush_lid_time": 2462,
|
||||
"rag_time": 0,
|
||||
"roll_brush_time": 2719,
|
||||
"sensor_time": 935
|
||||
},
|
||||
"getCurrentVoiceLanguage": {
|
||||
"name": "bb053ca2c5605a55090fcdb952f3902b",
|
||||
"version": 2
|
||||
},
|
||||
"getDoNotDisturb": {
|
||||
"do_not_disturb": true,
|
||||
"e_min": 480,
|
||||
"s_min": 1320
|
||||
},
|
||||
"getMapData": {
|
||||
"area_list": [
|
||||
{
|
||||
"cistern": 1,
|
||||
"clean_number": 1,
|
||||
"color": 3,
|
||||
"floor_texture": -1,
|
||||
"id": 5,
|
||||
"name": "I01BU0tFRF9OQU1FIw==",
|
||||
"suction": 2,
|
||||
"type": "room"
|
||||
},
|
||||
{
|
||||
"cistern": 1,
|
||||
"clean_number": 1,
|
||||
"color": 4,
|
||||
"floor_texture": -1,
|
||||
"id": 6,
|
||||
"name": "I01BU0tFRF9OQU1FIw==",
|
||||
"suction": 2,
|
||||
"type": "room"
|
||||
},
|
||||
{
|
||||
"cistern": 1,
|
||||
"clean_number": 1,
|
||||
"color": 1,
|
||||
"floor_texture": 0,
|
||||
"id": 2,
|
||||
"name": "I01BU0tFRF9OQU1FIw==",
|
||||
"suction": 2,
|
||||
"type": "room"
|
||||
},
|
||||
{
|
||||
"cistern": 1,
|
||||
"clean_number": 1,
|
||||
"color": 5,
|
||||
"floor_texture": 90,
|
||||
"id": 3,
|
||||
"name": "I01BU0tFRF9OQU1FIw==",
|
||||
"suction": 2,
|
||||
"type": "room"
|
||||
},
|
||||
{
|
||||
"cistern": 1,
|
||||
"clean_number": 1,
|
||||
"color": 2,
|
||||
"floor_texture": -1,
|
||||
"id": 4,
|
||||
"name": "I01BU0tFRF9OQU1FIw==",
|
||||
"suction": 2,
|
||||
"type": "room"
|
||||
},
|
||||
{
|
||||
"id": 401,
|
||||
"type": "virtual_wall",
|
||||
"vertexs": [
|
||||
[
|
||||
4711,
|
||||
985
|
||||
],
|
||||
[
|
||||
4717,
|
||||
-404
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 301,
|
||||
"type": "forbid",
|
||||
"vertexs": [
|
||||
[
|
||||
3061,
|
||||
-3027
|
||||
],
|
||||
[
|
||||
3580,
|
||||
-3027
|
||||
],
|
||||
[
|
||||
3580,
|
||||
-3692
|
||||
],
|
||||
[
|
||||
3061,
|
||||
-3692
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 402,
|
||||
"type": "virtual_wall",
|
||||
"vertexs": [
|
||||
[
|
||||
5302,
|
||||
6816
|
||||
],
|
||||
[
|
||||
5304,
|
||||
4924
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"cistern": -1,
|
||||
"clean_number": 1,
|
||||
"id": 501,
|
||||
"suction": -1,
|
||||
"type": "area",
|
||||
"vertexs": [
|
||||
[
|
||||
2889,
|
||||
6241
|
||||
],
|
||||
[
|
||||
3721,
|
||||
6241
|
||||
],
|
||||
[
|
||||
3721,
|
||||
4919
|
||||
],
|
||||
[
|
||||
2889,
|
||||
4919
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"carpet_strategy": 11,
|
||||
"id": 101,
|
||||
"type": "carpet_rectangle",
|
||||
"vertexs": [
|
||||
[
|
||||
20,
|
||||
-2012
|
||||
],
|
||||
[
|
||||
2857,
|
||||
-2012
|
||||
],
|
||||
[
|
||||
2857,
|
||||
-4122
|
||||
],
|
||||
[
|
||||
20,
|
||||
-4122
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"carpet_strategy": 11,
|
||||
"id": 102,
|
||||
"type": "carpet_rectangle",
|
||||
"vertexs": [
|
||||
[
|
||||
1327,
|
||||
3064
|
||||
],
|
||||
[
|
||||
2428,
|
||||
3064
|
||||
],
|
||||
[
|
||||
2428,
|
||||
2258
|
||||
],
|
||||
[
|
||||
1327,
|
||||
2258
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"carpet_strategy": 11,
|
||||
"id": 103,
|
||||
"type": "carpet_rectangle",
|
||||
"vertexs": [
|
||||
[
|
||||
4458,
|
||||
5974
|
||||
],
|
||||
[
|
||||
5336,
|
||||
5974
|
||||
],
|
||||
[
|
||||
5336,
|
||||
4903
|
||||
],
|
||||
[
|
||||
4458,
|
||||
4903
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"carpet_strategy": 11,
|
||||
"id": 104,
|
||||
"type": "carpet_rectangle",
|
||||
"vertexs": [
|
||||
[
|
||||
-1383,
|
||||
2730
|
||||
],
|
||||
[
|
||||
-761,
|
||||
2730
|
||||
],
|
||||
[
|
||||
-761,
|
||||
1587
|
||||
],
|
||||
[
|
||||
-1383,
|
||||
1587
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"auto_area_flag": true,
|
||||
"bit_list": {
|
||||
"auto_area": [
|
||||
0,
|
||||
100
|
||||
],
|
||||
"barrier": 0,
|
||||
"clean": 255,
|
||||
"none": 127
|
||||
},
|
||||
"bitnum": 8,
|
||||
"charge_coor": [
|
||||
65,
|
||||
134,
|
||||
272
|
||||
],
|
||||
"furniture_list": [],
|
||||
"height": 303,
|
||||
"map_data": "#SCRUBBED_MAPDATA#",
|
||||
"map_hash": "A5D8FA4487CC40312EF58D8123F0A4CC",
|
||||
"map_id": 1734727686,
|
||||
"map_locked": 0,
|
||||
"map_name": "I01BU0tFRF9OQU1FIw==",
|
||||
"origin_coor": [
|
||||
-33,
|
||||
-108,
|
||||
270
|
||||
],
|
||||
"path_id": 122,
|
||||
"pix_len": 66660,
|
||||
"pix_lz4len": 6826,
|
||||
"real_charge_coor": [
|
||||
1599,
|
||||
1295,
|
||||
272
|
||||
],
|
||||
"real_origin_coor": [
|
||||
-1674,
|
||||
-5424,
|
||||
270
|
||||
],
|
||||
"real_vac_coor": [
|
||||
1599,
|
||||
1076,
|
||||
272
|
||||
],
|
||||
"resolution": 50,
|
||||
"resolution_unit": "mm",
|
||||
"vac_coor": [
|
||||
65,
|
||||
130,
|
||||
272
|
||||
],
|
||||
"version": "LDS",
|
||||
"width": 220
|
||||
},
|
||||
"getMapInfo": {
|
||||
"auto_change_map": true,
|
||||
"current_map_id": 1734727686,
|
||||
"map_list": [
|
||||
{
|
||||
"auto_area_flag": true,
|
||||
"global_cleaned": -1,
|
||||
"is_saved": true,
|
||||
"map_id": 1734727686,
|
||||
"map_locked": 0,
|
||||
"map_name": "I01BU0tFRF9OQU1FIw==",
|
||||
"rotate_angle": 270,
|
||||
"update_time": 1737387285
|
||||
},
|
||||
{
|
||||
"auto_area_flag": true,
|
||||
"global_cleaned": -1,
|
||||
"is_saved": true,
|
||||
"map_id": 1734742958,
|
||||
"map_locked": 0,
|
||||
"map_name": "I01BU0tFRF9OQU1FIw==",
|
||||
"rotate_angle": 0,
|
||||
"update_time": 1737304392
|
||||
},
|
||||
{
|
||||
"auto_area_flag": true,
|
||||
"global_cleaned": -1,
|
||||
"is_saved": true,
|
||||
"map_id": 1736541042,
|
||||
"map_locked": 0,
|
||||
"map_name": "I01BU0tFRF9OQU1FIw==",
|
||||
"rotate_angle": 270,
|
||||
"update_time": 1737216718
|
||||
}
|
||||
],
|
||||
"map_num": 3,
|
||||
"version": "LDS"
|
||||
},
|
||||
"getMopState": {
|
||||
"mop_state": false
|
||||
},
|
||||
"getVacStatus": {
|
||||
"err_status": [
|
||||
0
|
||||
],
|
||||
"errorCode_id": [
|
||||
1144500830
|
||||
],
|
||||
"prompt": [],
|
||||
"promptCode_id": [],
|
||||
"status": 6
|
||||
},
|
||||
"getVolume": {
|
||||
"volume": 60
|
||||
},
|
||||
"get_device_info": {
|
||||
"auto_pack_ver": "0.0.131.1852",
|
||||
"avatar": "",
|
||||
"board_sn": "000000000000",
|
||||
"cd": "I01BU0tFRF9CSU5BUlkj",
|
||||
"custom_sn": "000000000000",
|
||||
"device_id": "0000000000000000000000000000000000000000",
|
||||
"fw_id": "00000000000000000000000000000000",
|
||||
"fw_ver": "1.2.0 Build 241219 Rel.163928",
|
||||
"has_set_location_info": true,
|
||||
"hw_id": "00000000000000000000000000000000",
|
||||
"hw_ver": "1.0",
|
||||
"ip": "127.0.0.123",
|
||||
"lang": "",
|
||||
"latitude": 0,
|
||||
"linux_ver": "V21.198.1708420747",
|
||||
"location": "",
|
||||
"longitude": 0,
|
||||
"mac": "7C-F1-7E-00-00-00",
|
||||
"mcu_ver": "1.1.2724.442",
|
||||
"model": "RV30 Max",
|
||||
"nickname": "I01BU0tFRF9OQU1FIw==",
|
||||
"oem_id": "00000000000000000000000000000000",
|
||||
"overheated": false,
|
||||
"product_id": "1794",
|
||||
"region": "America/Chicago",
|
||||
"rssi": -38,
|
||||
"signal_level": 3,
|
||||
"specs": "",
|
||||
"ssid": "I01BU0tFRF9TU0lEIw==",
|
||||
"sub_ver": "0.0.131.1852-1.4.40",
|
||||
"time_diff": -360,
|
||||
"total_ver": "1.4.40",
|
||||
"type": "SMART.TAPOROBOVAC"
|
||||
},
|
||||
"get_device_time": {
|
||||
"region": "America/Chicago",
|
||||
"time_diff": -360,
|
||||
"timestamp": 1737399953
|
||||
},
|
||||
"get_fw_download_state": {
|
||||
"auto_upgrade": false,
|
||||
"download_progress": 0,
|
||||
"reboot_time": 5,
|
||||
"status": 0,
|
||||
"upgrade_time": 5
|
||||
},
|
||||
"get_inherit_info": {
|
||||
"inherit_status": true
|
||||
},
|
||||
"get_latest_fw": {
|
||||
"fw_size": 0,
|
||||
"fw_ver": "1.2.0 Build 241219 Rel.163928",
|
||||
"hw_id": "",
|
||||
"need_to_upgrade": false,
|
||||
"oem_id": "",
|
||||
"release_date": "",
|
||||
"release_note": "",
|
||||
"type": 0
|
||||
},
|
||||
"get_schedule_rules": {
|
||||
"enable": true,
|
||||
"rule_list": [
|
||||
{
|
||||
"alarm_min": 0,
|
||||
"cancel": false,
|
||||
"clean_attr": {
|
||||
"cistern": 2,
|
||||
"clean_mode": 0,
|
||||
"clean_number": 1,
|
||||
"clean_order": false,
|
||||
"suction": 2
|
||||
},
|
||||
"day": 21,
|
||||
"enable": true,
|
||||
"id": "S1",
|
||||
"invalid": 0,
|
||||
"mode": "repeat",
|
||||
"month": 1,
|
||||
"s_min": 515,
|
||||
"start_remind": true,
|
||||
"week_day": 62,
|
||||
"year": 2025
|
||||
}
|
||||
],
|
||||
"schedule_rule_max_count": 32,
|
||||
"start_index": 0,
|
||||
"sum": 1
|
||||
},
|
||||
"get_wireless_scan_info": {
|
||||
"ap_list": [
|
||||
{
|
||||
"key_type": "wpa2_psk",
|
||||
"signal_level": 3,
|
||||
"ssid": "I01BU0tFRF9TU0lEIw=="
|
||||
},
|
||||
{
|
||||
"key_type": "wpa2_psk",
|
||||
"signal_level": 1,
|
||||
"ssid": "I01BU0tFRF9TU0lEIw=="
|
||||
},
|
||||
{
|
||||
"key_type": "wpa2_psk",
|
||||
"signal_level": 1,
|
||||
"ssid": "I01BU0tFRF9TU0lEIw=="
|
||||
},
|
||||
{
|
||||
"key_type": "wpa2_psk",
|
||||
"signal_level": 1,
|
||||
"ssid": "I01BU0tFRF9TU0lEIw=="
|
||||
},
|
||||
{
|
||||
"key_type": "wpa2_psk",
|
||||
"signal_level": 1,
|
||||
"ssid": "I01BU0tFRF9TU0lEIw=="
|
||||
}
|
||||
],
|
||||
"start_index": 0,
|
||||
"sum": 5,
|
||||
"wep_supported": true
|
||||
},
|
||||
"qs_component_nego": {
|
||||
"component_list": [
|
||||
{
|
||||
"id": "quick_setup",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "iot_cloud",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "firmware",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "ble_whole_setup",
|
||||
"ver_code": 1
|
||||
},
|
||||
{
|
||||
"id": "inherit",
|
||||
"ver_code": 1
|
||||
}
|
||||
],
|
||||
"extra_info": {
|
||||
"device_model": "RV30 Max",
|
||||
"device_type": "SMART.TAPOROBOVAC"
|
||||
}
|
||||
}
|
||||
}
|
@ -117,6 +117,10 @@ async def test_actions(
|
||||
async def test_post_update_hook(dev: SmartDevice, err_status: list, error: ErrorCode):
|
||||
"""Test that post update hook sets error states correctly."""
|
||||
clean = next(get_parent_and_child_modules(dev, Module.Clean))
|
||||
assert clean
|
||||
|
||||
# _post_update_hook will pop an item off the status list so create a copy.
|
||||
err_status = [e for e in err_status]
|
||||
clean.data["getVacStatus"]["err_status"] = err_status
|
||||
|
||||
await clean._post_update_hook()
|
||||
|
@ -1308,11 +1308,11 @@ async def test_discover_config(dev: Device, mocker, runner):
|
||||
expected = f"--device-family {cparam.device_family.value} --encrypt-type {cparam.encryption_type.value} {'--https' if cparam.https else '--no-https'}"
|
||||
assert expected in res.output
|
||||
assert re.search(
|
||||
r"Attempt to connect to 127\.0\.0\.1 with \w+ \+ \w+ \+ \w+ failed",
|
||||
r"Attempt to connect to 127\.0\.0\.1 with \w+ \+ \w+ \+ \w+ \+ \w+ failed",
|
||||
res.output.replace("\n", ""),
|
||||
)
|
||||
assert re.search(
|
||||
r"Attempt to connect to 127\.0\.0\.1 with \w+ \+ \w+ \+ \w+ succeeded",
|
||||
r"Attempt to connect to 127\.0\.0\.1 with \w+ \+ \w+ \+ \w+ \+ \w+ succeeded",
|
||||
res.output.replace("\n", ""),
|
||||
)
|
||||
|
||||
|
@ -63,8 +63,9 @@ def _get_connection_type_device_class(discovery_info):
|
||||
connection_type = DeviceConnectionParameters.from_values(
|
||||
dr.device_type,
|
||||
dr.mgt_encrypt_schm.encrypt_type,
|
||||
dr.mgt_encrypt_schm.lv,
|
||||
dr.mgt_encrypt_schm.is_support_https,
|
||||
login_version=dr.mgt_encrypt_schm.lv,
|
||||
https=dr.mgt_encrypt_schm.is_support_https,
|
||||
http_port=dr.mgt_encrypt_schm.http_port,
|
||||
)
|
||||
else:
|
||||
connection_type = DeviceConnectionParameters.from_values(
|
||||
|
@ -157,14 +157,15 @@ async def test_discover_single(discovery_mock, custom_port, mocker):
|
||||
)
|
||||
# Make sure discovery does not call update()
|
||||
assert update_mock.call_count == 0
|
||||
if discovery_mock.default_port == 80:
|
||||
if discovery_mock.default_port != 9999:
|
||||
assert x.alias is None
|
||||
|
||||
ct = DeviceConnectionParameters.from_values(
|
||||
discovery_mock.device_type,
|
||||
discovery_mock.encrypt_type,
|
||||
discovery_mock.login_version,
|
||||
discovery_mock.https,
|
||||
login_version=discovery_mock.login_version,
|
||||
https=discovery_mock.https,
|
||||
http_port=discovery_mock.http_port,
|
||||
)
|
||||
config = DeviceConfig(
|
||||
host=host,
|
||||
@ -425,9 +426,9 @@ async def test_discover_single_http_client(discovery_mock, mocker):
|
||||
|
||||
x: Device = await Discover.discover_single(host)
|
||||
|
||||
assert x.config.uses_http == (discovery_mock.default_port == 80)
|
||||
assert x.config.uses_http == (discovery_mock.default_port != 9999)
|
||||
|
||||
if discovery_mock.default_port == 80:
|
||||
if discovery_mock.default_port != 9999:
|
||||
assert x.protocol._transport._http_client.client != http_client
|
||||
x.config.http_client = http_client
|
||||
assert x.protocol._transport._http_client.client == http_client
|
||||
@ -442,9 +443,9 @@ async def test_discover_http_client(discovery_mock, mocker):
|
||||
|
||||
devices = await Discover.discover(discovery_timeout=0)
|
||||
x: Device = devices[host]
|
||||
assert x.config.uses_http == (discovery_mock.default_port == 80)
|
||||
assert x.config.uses_http == (discovery_mock.default_port != 9999)
|
||||
|
||||
if discovery_mock.default_port == 80:
|
||||
if discovery_mock.default_port != 9999:
|
||||
assert x.protocol._transport._http_client.client != http_client
|
||||
x.config.http_client = http_client
|
||||
assert x.protocol._transport._http_client.client == http_client
|
||||
@ -674,8 +675,9 @@ async def test_discover_try_connect_all(discovery_mock, mocker):
|
||||
cparams = DeviceConnectionParameters.from_values(
|
||||
discovery_mock.device_type,
|
||||
discovery_mock.encrypt_type,
|
||||
discovery_mock.login_version,
|
||||
discovery_mock.https,
|
||||
login_version=discovery_mock.login_version,
|
||||
https=discovery_mock.https,
|
||||
http_port=discovery_mock.http_port,
|
||||
)
|
||||
protocol = get_protocol(
|
||||
DeviceConfig(discovery_mock.ip, connection_type=cparams)
|
||||
@ -687,10 +689,13 @@ async def test_discover_try_connect_all(discovery_mock, mocker):
|
||||
protocol_class = IotProtocol
|
||||
transport_class = XorTransport
|
||||
|
||||
default_port = discovery_mock.default_port
|
||||
|
||||
async def _query(self, *args, **kwargs):
|
||||
if (
|
||||
self.__class__ is protocol_class
|
||||
and self._transport.__class__ is transport_class
|
||||
and self._transport._port == default_port
|
||||
):
|
||||
return discovery_mock.query_data
|
||||
raise KasaException("Unable to execute query")
|
||||
@ -699,6 +704,7 @@ async def test_discover_try_connect_all(discovery_mock, mocker):
|
||||
if (
|
||||
self.protocol.__class__ is protocol_class
|
||||
and self.protocol._transport.__class__ is transport_class
|
||||
and self.protocol._transport._port == default_port
|
||||
):
|
||||
return
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user