Add smartcamera devices to supported docs (#1257)

The library now officially supports H200, C200 and TC65
This commit is contained in:
Steven B. 2024-11-14 18:28:30 +00:00 committed by GitHub
parent b8f6651d9b
commit 5fe75cada9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 89 additions and 10 deletions

View File

@ -197,7 +197,8 @@ The following devices have been tested and confirmed as working. If your device
- **Wall Switches**: S500D, S505, S505D - **Wall Switches**: S500D, S505, S505D
- **Bulbs**: L510B, L510E, L530E, L630 - **Bulbs**: L510B, L510E, L530E, L630
- **Light Strips**: L900-10, L900-5, L920-5, L930-5 - **Light Strips**: L900-10, L900-5, L920-5, L930-5
- **Hubs**: H100 - **Cameras**: C210, TC65
- **Hubs**: H100, H200
- **Hub-Connected Devices<sup>\*\*\*</sup>**: S200B, S200D, T100, T110, T300, T310, T315 - **Hub-Connected Devices<sup>\*\*\*</sup>**: S200B, S200D, T100, T110, T300, T310, T315
<!--SUPPORTED_END--> <!--SUPPORTED_END-->

View File

@ -246,12 +246,23 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
- **L930-5** - **L930-5**
- Hardware: 1.0 (US) / Firmware: 1.1.2 - Hardware: 1.0 (US) / Firmware: 1.1.2
### Cameras
- **C210**
- Hardware: 2.0 (EU) / Firmware: 1.4.2
- Hardware: 2.0 (EU) / Firmware: 1.4.3
- **TC65**
- Hardware: 1.0 / Firmware: 1.3.9
### Hubs ### Hubs
- **H100** - **H100**
- Hardware: 1.0 (EU) / Firmware: 1.2.3 - Hardware: 1.0 (EU) / Firmware: 1.2.3
- Hardware: 1.0 (EU) / Firmware: 1.5.10 - Hardware: 1.0 (EU) / Firmware: 1.5.10
- Hardware: 1.0 (EU) / Firmware: 1.5.5 - Hardware: 1.0 (EU) / Firmware: 1.5.5
- **H200**
- Hardware: 1.0 (EU) / Firmware: 1.3.2
- Hardware: 1.0 (US) / Firmware: 1.3.6
### Hub-Connected Devices ### Hub-Connected Devices

View File

@ -46,6 +46,7 @@ from kasa.protocols.smartcameraprotocol import (
) )
from kasa.protocols.smartprotocol import SmartProtocol, _ChildProtocolWrapper from kasa.protocols.smartprotocol import SmartProtocol, _ChildProtocolWrapper
from kasa.smart import SmartChildDevice from kasa.smart import SmartChildDevice
from kasa.smartcamera import SmartCamera
Call = namedtuple("Call", "module method") Call = namedtuple("Call", "module method")
FixtureResult = namedtuple("FixtureResult", "filename, folder, data") FixtureResult = namedtuple("FixtureResult", "filename, folder, data")
@ -973,14 +974,12 @@ async def get_smart_fixtures(
copy_folder = SMART_FOLDER copy_folder = SMART_FOLDER
else: else:
# smart camera protocol # smart camera protocol
basic_info = final["getDeviceInfo"]["device_info"]["basic_info"] model_info = SmartCamera._get_device_info(final, discovery_info)
hw_version = basic_info["hw_version"] model = model_info.long_name
sw_version = basic_info["sw_version"] hw_version = model_info.hardware_version
model = basic_info["device_model"] sw_version = model_info.firmare_version
region = basic_info.get("region") if model_info.region is not None:
sw_version = sw_version.split(" ", maxsplit=1)[0] model = f"{model}({model_info.region})"
if region is not None:
model = f"{model}({region})"
copy_folder = SMARTCAMERA_FOLDER copy_folder = SMARTCAMERA_FOLDER
save_filename = f"{model}_{hw_version}_{sw_version}.json" save_filename = f"{model}_{hw_version}_{sw_version}.json"

View File

@ -10,7 +10,8 @@ from typing import 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.smart.smartdevice import SmartDevice from kasa.smart import SmartDevice
from kasa.smartcamera import SmartCamera
class SupportedVersion(NamedTuple): class SupportedVersion(NamedTuple):
@ -32,6 +33,7 @@ DEVICE_TYPE_TO_PRODUCT_GROUP = {
DeviceType.Fan: "Wall Switches", DeviceType.Fan: "Wall Switches",
DeviceType.Bulb: "Bulbs", DeviceType.Bulb: "Bulbs",
DeviceType.LightStrip: "Light Strips", DeviceType.LightStrip: "Light Strips",
DeviceType.Camera: "Cameras",
DeviceType.Hub: "Hubs", DeviceType.Hub: "Hubs",
DeviceType.Sensor: "Hub-Connected Devices", DeviceType.Sensor: "Hub-Connected Devices",
DeviceType.Thermostat: "Hub-Connected Devices", DeviceType.Thermostat: "Hub-Connected Devices",
@ -43,6 +45,7 @@ README_FILENAME = "README.md"
IOT_FOLDER = "tests/fixtures/" IOT_FOLDER = "tests/fixtures/"
SMART_FOLDER = "tests/fixtures/smart/" SMART_FOLDER = "tests/fixtures/smart/"
SMARTCAMERA_FOLDER = "tests/fixtures/smartcamera/"
def generate_supported(args): def generate_supported(args):
@ -58,6 +61,7 @@ def generate_supported(args):
_get_iot_supported(supported) _get_iot_supported(supported)
_get_smart_supported(supported) _get_smart_supported(supported)
_get_smartcamera_supported(supported)
readme_updated = _update_supported_file( readme_updated = _update_supported_file(
README_FILENAME, _supported_summary(supported), print_diffs README_FILENAME, _supported_summary(supported), print_diffs
@ -234,6 +238,29 @@ def _get_smart_supported(supported):
) )
def _get_smartcamera_supported(supported):
for file in Path(SMARTCAMERA_FOLDER).glob("**/*.json"):
with file.open() as f:
fixture_data = json.load(f)
model_info = SmartCamera._get_device_info(
fixture_data, fixture_data.get("discovery_result")
)
supported_type = DEVICE_TYPE_TO_PRODUCT_GROUP[model_info.device_type]
stype = supported[model_info.brand].setdefault(supported_type, {})
smodel = stype.setdefault(model_info.long_name, [])
smodel.append(
SupportedVersion(
region=model_info.region,
hw=model_info.hardware_version,
fw=model_info.firmare_version,
auth=model_info.requires_auth,
)
)
def _get_iot_supported(supported): def _get_iot_supported(supported):
for file in Path(IOT_FOLDER).glob("*.json"): for file in Path(IOT_FOLDER).glob("*.json"):
with file.open() as f: with file.open() as f:

View File

@ -152,6 +152,22 @@ class WifiNetwork:
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@dataclass
class _DeviceInfo:
"""Device Model Information."""
short_name: str
long_name: str
brand: str
device_family: str
device_type: DeviceType
hardware_version: str
firmare_version: str
firmware_build: str
requires_auth: bool
region: str | None
class Device(ABC): class Device(ABC):
"""Common device interface. """Common device interface.

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import logging import logging
from typing import Any from typing import Any
from ..device import _DeviceInfo
from ..device_type import DeviceType from ..device_type import DeviceType
from ..module import Module from ..module import Module
from ..protocols.smartcameraprotocol import _ChildCameraProtocolWrapper from ..protocols.smartcameraprotocol import _ChildCameraProtocolWrapper
@ -29,6 +30,30 @@ class SmartCamera(SmartDevice):
return DeviceType.Hub return DeviceType.Hub
return DeviceType.Camera return DeviceType.Camera
@staticmethod
def _get_device_info(
info: dict[str, Any], discovery_info: dict[str, Any] | None
) -> _DeviceInfo:
"""Get model information for a device."""
basic_info = info["getDeviceInfo"]["device_info"]["basic_info"]
short_name = basic_info["device_model"]
long_name = discovery_info["device_model"] if discovery_info else short_name
device_type = SmartCamera._get_device_type_from_sysinfo(basic_info)
fw_version_full = basic_info["sw_version"]
firmare_version, firmware_build = fw_version_full.split(" ", maxsplit=1)
return _DeviceInfo(
short_name=basic_info["device_model"],
long_name=long_name,
brand="tapo",
device_family=basic_info["device_type"],
device_type=device_type,
hardware_version=basic_info["hw_version"],
firmare_version=firmare_version,
firmware_build=firmware_build,
requires_auth=True,
region=basic_info.get("region"),
)
def _update_internal_info(self, info_resp: dict) -> None: def _update_internal_info(self, info_resp: dict) -> None:
"""Update the internal device info.""" """Update the internal device info."""
info = self._try_get_response(info_resp, "getDeviceInfo") info = self._try_get_response(info_resp, "getDeviceInfo")