mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Move TAPO smartcamera out of experimental package (#1255)
Co-authored-by: Teemu R. <tpr@iki.fi>
This commit is contained in:
parent
e55731c110
commit
6213b90f62
@ -40,7 +40,7 @@ from kasa.device_factory import get_protocol
|
|||||||
from kasa.deviceconfig import DeviceEncryptionType, DeviceFamily
|
from kasa.deviceconfig import DeviceEncryptionType, DeviceFamily
|
||||||
from kasa.discover import DiscoveryResult
|
from kasa.discover import DiscoveryResult
|
||||||
from kasa.exceptions import SmartErrorCode
|
from kasa.exceptions import SmartErrorCode
|
||||||
from kasa.experimental.smartcameraprotocol import (
|
from kasa.protocols.smartcameraprotocol import (
|
||||||
SmartCameraProtocol,
|
SmartCameraProtocol,
|
||||||
_ChildCameraProtocolWrapper,
|
_ChildCameraProtocolWrapper,
|
||||||
)
|
)
|
||||||
|
@ -10,9 +10,6 @@ from .device import Device
|
|||||||
from .device_type import DeviceType
|
from .device_type import DeviceType
|
||||||
from .deviceconfig import DeviceConfig
|
from .deviceconfig import DeviceConfig
|
||||||
from .exceptions import KasaException, UnsupportedDeviceError
|
from .exceptions import KasaException, UnsupportedDeviceError
|
||||||
from .experimental.smartcamera import SmartCamera
|
|
||||||
from .experimental.smartcameraprotocol import SmartCameraProtocol
|
|
||||||
from .experimental.sslaestransport import SslAesTransport
|
|
||||||
from .iot import (
|
from .iot import (
|
||||||
IotBulb,
|
IotBulb,
|
||||||
IotDevice,
|
IotDevice,
|
||||||
@ -27,7 +24,9 @@ from .protocols import (
|
|||||||
IotProtocol,
|
IotProtocol,
|
||||||
SmartProtocol,
|
SmartProtocol,
|
||||||
)
|
)
|
||||||
|
from .protocols.smartcameraprotocol import SmartCameraProtocol
|
||||||
from .smart import SmartDevice
|
from .smart import SmartDevice
|
||||||
|
from .smartcamera.smartcamera import SmartCamera
|
||||||
from .transports import (
|
from .transports import (
|
||||||
AesTransport,
|
AesTransport,
|
||||||
BaseTransport,
|
BaseTransport,
|
||||||
@ -35,6 +34,7 @@ from .transports import (
|
|||||||
KlapTransportV2,
|
KlapTransportV2,
|
||||||
XorTransport,
|
XorTransport,
|
||||||
)
|
)
|
||||||
|
from .transports.sslaestransport import SslAesTransport
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -217,12 +217,9 @@ def get_protocol(
|
|||||||
"IOT.KLAP": (IotProtocol, KlapTransport),
|
"IOT.KLAP": (IotProtocol, KlapTransport),
|
||||||
"SMART.AES": (SmartProtocol, AesTransport),
|
"SMART.AES": (SmartProtocol, AesTransport),
|
||||||
"SMART.KLAP": (SmartProtocol, KlapTransportV2),
|
"SMART.KLAP": (SmartProtocol, KlapTransportV2),
|
||||||
|
"SMART.AES.HTTPS": (SmartCameraProtocol, SslAesTransport),
|
||||||
}
|
}
|
||||||
if not (prot_tran_cls := supported_device_protocols.get(protocol_transport_key)):
|
if not (prot_tran_cls := supported_device_protocols.get(protocol_transport_key)):
|
||||||
from .experimental import Experimental
|
return None
|
||||||
|
protocol_cls, transport_cls = prot_tran_cls
|
||||||
if Experimental.enabled() and protocol_transport_key == "SMART.AES.HTTPS":
|
return protocol_cls(transport=transport_cls(config=config))
|
||||||
prot_tran_cls = (SmartCameraProtocol, SslAesTransport)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
return prot_tran_cls[0](transport=prot_tran_cls[1](config=config))
|
|
||||||
|
@ -55,9 +55,9 @@ from .modulemapping import ModuleName
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import interfaces
|
from . import interfaces
|
||||||
from .device import Device
|
from .device import Device
|
||||||
from .experimental import modules as experimental
|
|
||||||
from .iot import modules as iot
|
from .iot import modules as iot
|
||||||
from .smart import modules as smart
|
from .smart import modules as smart
|
||||||
|
from .smartcamera import modules as smartcamera
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ class Module(ABC):
|
|||||||
TriggerLogs: Final[ModuleName[smart.TriggerLogs]] = ModuleName("TriggerLogs")
|
TriggerLogs: Final[ModuleName[smart.TriggerLogs]] = ModuleName("TriggerLogs")
|
||||||
|
|
||||||
# SMARTCAMERA only modules
|
# SMARTCAMERA only modules
|
||||||
Camera: Final[ModuleName[experimental.Camera]] = ModuleName("Camera")
|
Camera: Final[ModuleName[smartcamera.Camera]] = ModuleName("Camera")
|
||||||
|
|
||||||
def __init__(self, device: Device, module: str) -> None:
|
def __init__(self, device: Device, module: str) -> None:
|
||||||
self._device = device
|
self._device = device
|
||||||
|
@ -14,12 +14,12 @@ from ..exceptions import (
|
|||||||
_RetryableError,
|
_RetryableError,
|
||||||
)
|
)
|
||||||
from ..json import dumps as json_dumps
|
from ..json import dumps as json_dumps
|
||||||
from ..protocols import SmartProtocol
|
from ..transports.sslaestransport import (
|
||||||
from .sslaestransport import (
|
|
||||||
SMART_AUTHENTICATION_ERRORS,
|
SMART_AUTHENTICATION_ERRORS,
|
||||||
SMART_RETRYABLE_ERRORS,
|
SMART_RETRYABLE_ERRORS,
|
||||||
SmartErrorCode,
|
SmartErrorCode,
|
||||||
)
|
)
|
||||||
|
from . import SmartProtocol
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
5
kasa/smartcamera/__init__.py
Normal file
5
kasa/smartcamera/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
"""Package for supporting tapo-branded cameras."""
|
||||||
|
|
||||||
|
from .smartcamera import SmartCamera
|
||||||
|
|
||||||
|
__all__ = ["SmartCamera"]
|
@ -7,11 +7,10 @@ from typing import Any
|
|||||||
|
|
||||||
from ..device_type import DeviceType
|
from ..device_type import DeviceType
|
||||||
from ..module import Module
|
from ..module import Module
|
||||||
|
from ..protocols.smartcameraprotocol import _ChildCameraProtocolWrapper
|
||||||
from ..smart import SmartChildDevice, SmartDevice
|
from ..smart import SmartChildDevice, SmartDevice
|
||||||
from .modules.childdevice import ChildDevice
|
from .modules import ChildDevice, DeviceModule
|
||||||
from .modules.device import DeviceModule
|
|
||||||
from .smartcameramodule import SmartCameraModule
|
from .smartcameramodule import SmartCameraModule
|
||||||
from .smartcameraprotocol import _ChildCameraProtocolWrapper
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
@ -27,7 +27,7 @@ from ..exceptions import (
|
|||||||
from ..httpclient import HttpClient
|
from ..httpclient import HttpClient
|
||||||
from ..json import dumps as json_dumps
|
from ..json import dumps as json_dumps
|
||||||
from ..json import loads as json_loads
|
from ..json import loads as json_loads
|
||||||
from ..transports import AesEncyptionSession, BaseTransport
|
from . import AesEncyptionSession, BaseTransport
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
@ -11,9 +11,9 @@ from kasa import (
|
|||||||
DeviceType,
|
DeviceType,
|
||||||
Discover,
|
Discover,
|
||||||
)
|
)
|
||||||
from kasa.experimental.smartcamera import SmartCamera
|
|
||||||
from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip, IotWallSwitch
|
from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip, IotWallSwitch
|
||||||
from kasa.smart import SmartDevice
|
from kasa.smart import SmartDevice
|
||||||
|
from kasa.smartcamera.smartcamera import SmartCamera
|
||||||
|
|
||||||
from .fakeprotocol_iot import FakeIotProtocol
|
from .fakeprotocol_iot import FakeIotProtocol
|
||||||
from .fakeprotocol_smart import FakeSmartProtocol
|
from .fakeprotocol_smart import FakeSmartProtocol
|
||||||
|
@ -10,6 +10,7 @@ from kasa.transports.xortransport import XorEncryption
|
|||||||
|
|
||||||
from .fakeprotocol_iot import FakeIotProtocol
|
from .fakeprotocol_iot import FakeIotProtocol
|
||||||
from .fakeprotocol_smart import FakeSmartProtocol, FakeSmartTransport
|
from .fakeprotocol_smart import FakeSmartProtocol, FakeSmartTransport
|
||||||
|
from .fakeprotocol_smartcamera import FakeSmartCameraProtocol
|
||||||
from .fixtureinfo import FixtureInfo, filter_fixtures, idgenerator
|
from .fixtureinfo import FixtureInfo, filter_fixtures, idgenerator
|
||||||
|
|
||||||
DISCOVERY_MOCK_IP = "127.0.0.123"
|
DISCOVERY_MOCK_IP = "127.0.0.123"
|
||||||
@ -126,12 +127,14 @@ def create_discovery_mock(ip: str, fixture_data: dict):
|
|||||||
|
|
||||||
if "discovery_result" in fixture_data:
|
if "discovery_result" in fixture_data:
|
||||||
discovery_data = {"result": fixture_data["discovery_result"].copy()}
|
discovery_data = {"result": fixture_data["discovery_result"].copy()}
|
||||||
device_type = fixture_data["discovery_result"]["device_type"]
|
discovery_result = fixture_data["discovery_result"]
|
||||||
encrypt_type = fixture_data["discovery_result"]["mgt_encrypt_schm"][
|
device_type = discovery_result["device_type"]
|
||||||
"encrypt_type"
|
encrypt_type = discovery_result["mgt_encrypt_schm"].get(
|
||||||
]
|
"encrypt_type", discovery_result.get("encrypt_info", {}).get("sym_schm")
|
||||||
login_version = fixture_data["discovery_result"]["mgt_encrypt_schm"].get("lv")
|
)
|
||||||
https = fixture_data["discovery_result"]["mgt_encrypt_schm"]["is_support_https"]
|
|
||||||
|
login_version = discovery_result["mgt_encrypt_schm"].get("lv")
|
||||||
|
https = discovery_result["mgt_encrypt_schm"]["is_support_https"]
|
||||||
dm = _DiscoveryMock(
|
dm = _DiscoveryMock(
|
||||||
ip,
|
ip,
|
||||||
80,
|
80,
|
||||||
@ -172,7 +175,9 @@ def patch_discovery(fixture_infos: dict[str, FixtureInfo], mocker):
|
|||||||
}
|
}
|
||||||
protos = {
|
protos = {
|
||||||
ip: FakeSmartProtocol(fixture_info.data, fixture_info.name)
|
ip: FakeSmartProtocol(fixture_info.data, fixture_info.name)
|
||||||
if "SMART" in fixture_info.protocol
|
if fixture_info.protocol in {"SMART", "SMART.CHILD"}
|
||||||
|
else FakeSmartCameraProtocol(fixture_info.data, fixture_info.name)
|
||||||
|
if fixture_info.protocol in {"SMARTCAMERA", "SMARTCAMERA.CHILD"}
|
||||||
else FakeIotProtocol(fixture_info.data, fixture_info.name)
|
else FakeIotProtocol(fixture_info.data, fixture_info.name)
|
||||||
for ip, fixture_info in fixture_infos.items()
|
for ip, fixture_info in fixture_infos.items()
|
||||||
}
|
}
|
||||||
@ -197,7 +202,9 @@ def patch_discovery(fixture_infos: dict[str, FixtureInfo], mocker):
|
|||||||
# update the protos for any host testing or the test overriding the first ip
|
# update the protos for any host testing or the test overriding the first ip
|
||||||
protos[host] = (
|
protos[host] = (
|
||||||
FakeSmartProtocol(fixture_info.data, fixture_info.name)
|
FakeSmartProtocol(fixture_info.data, fixture_info.name)
|
||||||
if "SMART" in fixture_info.protocol
|
if fixture_info.protocol in {"SMART", "SMART.CHILD"}
|
||||||
|
else FakeSmartCameraProtocol(fixture_info.data, fixture_info.name)
|
||||||
|
if fixture_info.protocol in {"SMARTCAMERA", "SMARTCAMERA.CHILD"}
|
||||||
else FakeIotProtocol(fixture_info.data, fixture_info.name)
|
else FakeIotProtocol(fixture_info.data, fixture_info.name)
|
||||||
)
|
)
|
||||||
port = (
|
port = (
|
||||||
|
@ -4,7 +4,7 @@ import copy
|
|||||||
from json import loads as json_loads
|
from json import loads as json_loads
|
||||||
|
|
||||||
from kasa import Credentials, DeviceConfig, SmartProtocol
|
from kasa import Credentials, DeviceConfig, SmartProtocol
|
||||||
from kasa.experimental.smartcameraprotocol import SmartCameraProtocol
|
from kasa.protocols.smartcameraprotocol import SmartCameraProtocol
|
||||||
from kasa.transports.basetransport import BaseTransport
|
from kasa.transports.basetransport import BaseTransport
|
||||||
|
|
||||||
from .fakeprotocol_smart import FakeSmartTransport
|
from .fakeprotocol_smart import FakeSmartTransport
|
||||||
@ -136,6 +136,12 @@ class FakeSmartCameraTransport(BaseTransport):
|
|||||||
"basic",
|
"basic",
|
||||||
"zone_id",
|
"zone_id",
|
||||||
],
|
],
|
||||||
|
("led", "config", "enabled"): [
|
||||||
|
"getLedStatus",
|
||||||
|
"led",
|
||||||
|
"config",
|
||||||
|
"enabled",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _send_request(self, request_dict: dict):
|
async def _send_request(self, request_dict: dict):
|
||||||
|
@ -8,8 +8,8 @@ from typing import Iterable, NamedTuple
|
|||||||
|
|
||||||
from kasa.device_factory import _get_device_type_from_sys_info
|
from kasa.device_factory import _get_device_type_from_sys_info
|
||||||
from kasa.device_type import DeviceType
|
from kasa.device_type import DeviceType
|
||||||
from kasa.experimental.smartcamera import SmartCamera
|
|
||||||
from kasa.smart.smartdevice import SmartDevice
|
from kasa.smart.smartdevice import SmartDevice
|
||||||
|
from kasa.smartcamera.smartcamera import SmartCamera
|
||||||
|
|
||||||
|
|
||||||
class FixtureInfo(NamedTuple):
|
class FixtureInfo(NamedTuple):
|
||||||
@ -179,7 +179,7 @@ def filter_fixtures(
|
|||||||
|
|
||||||
filtered = []
|
filtered = []
|
||||||
if protocol_filter is None:
|
if protocol_filter is None:
|
||||||
protocol_filter = {"IOT", "SMART"}
|
protocol_filter = {"IOT", "SMART", "SMARTCAMERA"}
|
||||||
for fixture_data in fixture_list:
|
for fixture_data in fixture_list:
|
||||||
if data_root_filter and data_root_filter not in fixture_data.data:
|
if data_root_filter and data_root_filter not in fixture_data.data:
|
||||||
continue
|
continue
|
||||||
|
@ -43,6 +43,7 @@ from kasa.cli.wifi import wifi
|
|||||||
from kasa.discover import Discover, DiscoveryResult
|
from kasa.discover import Discover, DiscoveryResult
|
||||||
from kasa.iot import IotDevice
|
from kasa.iot import IotDevice
|
||||||
from kasa.smart import SmartDevice
|
from kasa.smart import SmartDevice
|
||||||
|
from kasa.smartcamera import SmartCamera
|
||||||
|
|
||||||
from .conftest import (
|
from .conftest import (
|
||||||
device_smart,
|
device_smart,
|
||||||
@ -178,6 +179,9 @@ async def test_state(dev, turn_on, runner):
|
|||||||
|
|
||||||
@turn_on
|
@turn_on
|
||||||
async def test_toggle(dev, turn_on, runner):
|
async def test_toggle(dev, turn_on, runner):
|
||||||
|
if isinstance(dev, SmartCamera) and dev.device_type == DeviceType.Hub:
|
||||||
|
pytest.skip(reason="Hub cannot toggle state")
|
||||||
|
|
||||||
await handle_turn_on(dev, turn_on)
|
await handle_turn_on(dev, turn_on)
|
||||||
await dev.update()
|
await dev.update()
|
||||||
assert dev.is_on == turn_on
|
assert dev.is_on == turn_on
|
||||||
@ -208,7 +212,9 @@ async def test_raw_command(dev, mocker, runner):
|
|||||||
update = mocker.patch.object(dev, "update")
|
update = mocker.patch.object(dev, "update")
|
||||||
from kasa.smart import SmartDevice
|
from kasa.smart import SmartDevice
|
||||||
|
|
||||||
if isinstance(dev, SmartDevice):
|
if isinstance(dev, SmartCamera):
|
||||||
|
params = ["na", "getDeviceInfo"]
|
||||||
|
elif isinstance(dev, SmartDevice):
|
||||||
params = ["na", "get_device_info"]
|
params = ["na", "get_device_info"]
|
||||||
else:
|
else:
|
||||||
params = ["system", "get_sysinfo"]
|
params = ["system", "get_sysinfo"]
|
||||||
|
@ -19,6 +19,7 @@ from kasa import (
|
|||||||
)
|
)
|
||||||
from kasa.device_factory import (
|
from kasa.device_factory import (
|
||||||
Device,
|
Device,
|
||||||
|
SmartCamera,
|
||||||
SmartDevice,
|
SmartDevice,
|
||||||
_get_device_type_from_sys_info,
|
_get_device_type_from_sys_info,
|
||||||
connect,
|
connect,
|
||||||
@ -177,7 +178,9 @@ async def test_connect_http_client(discovery_mock, mocker):
|
|||||||
|
|
||||||
async def test_device_types(dev: Device):
|
async def test_device_types(dev: Device):
|
||||||
await dev.update()
|
await dev.update()
|
||||||
if isinstance(dev, SmartDevice):
|
if isinstance(dev, SmartCamera):
|
||||||
|
res = SmartCamera._get_device_type_from_sysinfo(dev.sys_info)
|
||||||
|
elif isinstance(dev, SmartDevice):
|
||||||
assert dev._discovery_info
|
assert dev._discovery_info
|
||||||
device_type = cast(str, dev._discovery_info["result"]["device_type"])
|
device_type = cast(str, dev._discovery_info["result"]["device_type"])
|
||||||
res = SmartDevice._get_device_type_from_components(
|
res = SmartDevice._get_device_type_from_components(
|
||||||
|
@ -18,13 +18,13 @@ from kasa.exceptions import (
|
|||||||
KasaException,
|
KasaException,
|
||||||
SmartErrorCode,
|
SmartErrorCode,
|
||||||
)
|
)
|
||||||
from kasa.experimental.sslaestransport import (
|
from kasa.httpclient import HttpClient
|
||||||
|
from kasa.transports.aestransport import AesEncyptionSession
|
||||||
|
from kasa.transports.sslaestransport import (
|
||||||
SslAesTransport,
|
SslAesTransport,
|
||||||
TransportState,
|
TransportState,
|
||||||
_sha256_hash,
|
_sha256_hash,
|
||||||
)
|
)
|
||||||
from kasa.httpclient import HttpClient
|
|
||||||
from kasa.transports.aestransport import AesEncyptionSession
|
|
||||||
|
|
||||||
# Transport tests are not designed for real devices
|
# Transport tests are not designed for real devices
|
||||||
pytestmark = [pytest.mark.requires_dummy]
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
Loading…
Reference in New Issue
Block a user