| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d02e017 commit e117a99
20 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -179,9 +179,9 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros | |||
| 179 | 179 | ### Plugs | |
| 180 | 180 | ||
| 181 | 181 | - **P100** | |
| 182 | - - Hardware: 1.0.0 / Firmware: 1.1.3 | ||
| 183 | - - Hardware: 1.0.0 / Firmware: 1.3.7 | ||
| 184 | - - Hardware: 1.0.0 / Firmware: 1.4.0 | ||
| 182 | + - Hardware: 1.0.0 (US) / Firmware: 1.1.3 | ||
| 183 | + - Hardware: 1.0.0 (US) / Firmware: 1.3.7 | ||
| 184 | + - Hardware: 1.0.0 (US) / Firmware: 1.4.0 | ||
| 185 | 185 | - **P110** | |
| 186 | 186 | - Hardware: 1.0 (EU) / Firmware: 1.0.7 | |
| 187 | 187 | - Hardware: 1.0 (EU) / Firmware: 1.2.3 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,6 +21,7 @@ | |||
| 21 | 21 | from collections import defaultdict, namedtuple | |
| 22 | 22 | from pathlib import Path | |
| 23 | 23 | from pprint import pprint | |
| 24 | + from typing import Any | ||
| 24 | 25 | ||
| 25 | 26 | import asyncclick as click | |
| 26 | 27 | ||
@@ -40,12 +41,13 @@ | |||
| 40 | 41 | from kasa.deviceconfig import DeviceEncryptionType, DeviceFamily | |
| 41 | 42 | from kasa.discover import DiscoveryResult | |
| 42 | 43 | from kasa.exceptions import SmartErrorCode | |
| 44 | + from kasa.protocols import IotProtocol | ||
| 43 | 45 | from kasa.protocols.smartcameraprotocol import ( | |
| 44 | 46 | SmartCameraProtocol, | |
| 45 | 47 | _ChildCameraProtocolWrapper, | |
| 46 | 48 | ) | |
| 47 | 49 | from kasa.protocols.smartprotocol import SmartProtocol, _ChildProtocolWrapper | |
| 48 | - from kasa.smart import SmartChildDevice | ||
| 50 | + from kasa.smart import SmartChildDevice, SmartDevice | ||
| 49 | 51 | from kasa.smartcamera import SmartCamera | |
| 50 | 52 | ||
| 51 | 53 | Call = namedtuple("Call", "module method") | |
@@ -389,7 +391,9 @@ async def cli( | |||
| 389 | 391 | ) | |
| 390 | 392 | ||
| 391 | 393 | ||
| 392 | - async def get_legacy_fixture(protocol, *, discovery_info): | ||
| 394 | + async def get_legacy_fixture( | ||
| 395 | + protocol: IotProtocol, *, discovery_info: dict[str, Any] | None | ||
| 396 | + ) -> FixtureResult: | ||
| 393 | 397 | """Get fixture for legacy IOT style protocol.""" | |
| 394 | 398 | items = [ | |
| 395 | 399 | Call(module="system", method="get_sysinfo"), | |
@@ -422,8 +426,8 @@ async def get_legacy_fixture(protocol, *, discovery_info): | |||
| 422 | 426 | finally: | |
| 423 | 427 | await protocol.close() | |
| 424 | 428 | ||
| 425 | - final_query = defaultdict(defaultdict) | ||
| 426 | - final = defaultdict(defaultdict) | ||
| 429 | + final_query: dict = defaultdict(defaultdict) | ||
| 430 | + final: dict = defaultdict(defaultdict) | ||
| 427 | 431 | for succ, resp in successes: | |
| 428 | 432 | final_query[succ.module][succ.method] = {} | |
| 429 | 433 | final[succ.module][succ.method] = resp | |
@@ -433,16 +437,14 @@ async def get_legacy_fixture(protocol, *, discovery_info): | |||
| 433 | 437 | try: | |
| 434 | 438 | final = await protocol.query(final_query) | |
| 435 | 439 | except Exception as ex: | |
| 436 | - _echo_error(f"Unable to query all successes at once: {ex}", bold=True, fg="red") | ||
| 440 | + _echo_error(f"Unable to query all successes at once: {ex}") | ||
| 437 | 441 | finally: | |
| 438 | 442 | await protocol.close() | |
| 439 | 443 | if discovery_info and not discovery_info.get("system"): | |
| 440 | 444 | # Need to recreate a DiscoverResult here because we don't want the aliases | |
| 441 | 445 | # in the fixture, we want the actual field names as returned by the device. | |
| 442 | - dr = DiscoveryResult.from_dict(protocol._discovery_info) | ||
| 443 | - final["discovery_result"] = dr.dict( | ||
| 444 | - by_alias=False, exclude_unset=True, exclude_none=True, exclude_defaults=True | ||
| 445 | - ) | ||
| 446 | + dr = DiscoveryResult.from_dict(discovery_info) | ||
| 447 | + final["discovery_result"] = dr.to_dict() | ||
| 446 | 448 | ||
| 447 | 449 | click.echo("Got %s successes" % len(successes)) | |
| 448 | 450 | click.echo(click.style("## device info file ##", bold=True)) | |
@@ -817,23 +819,21 @@ async def get_smart_test_calls(protocol: SmartProtocol): | |||
| 817 | 819 | ||
| 818 | 820 | def get_smart_child_fixture(response): | |
| 819 | 821 | """Get a seperate fixture for the child device.""" | |
| 820 | - info = response["get_device_info"] | ||
| 821 | - hw_version = info["hw_ver"] | ||
| 822 | - sw_version = info["fw_ver"] | ||
| 823 | - sw_version = sw_version.split(" ", maxsplit=1)[0] | ||
| 824 | - model = info["model"] | ||
| 825 | - if region := info.get("specs"): | ||
| 826 | - model += f"({region})" | ||
| 827 | - | ||
| 828 | - save_filename = f"{model}_{hw_version}_{sw_version}.json" | ||
| 822 | + model_info = SmartDevice._get_device_info(response, None) | ||
| 823 | + hw_version = model_info.hardware_version | ||
| 824 | + fw_version = model_info.firmware_version | ||
| 825 | + model = model_info.long_name | ||
| 826 | + if model_info.region is not None: | ||
| 827 | + model = f"{model}({model_info.region})" | ||
| 828 | + save_filename = f"{model}_{hw_version}_{fw_version}.json" | ||
| 829 | 829 | return FixtureResult( | |
| 830 | 830 | filename=save_filename, folder=SMART_CHILD_FOLDER, data=response | |
| 831 | 831 | ) | |
| 832 | 832 | ||
| 833 | 833 | ||
| 834 | 834 | async def get_smart_fixtures( | |
| 835 | - protocol: SmartProtocol, *, discovery_info=None, batch_size: int | ||
| 836 | - ): | ||
| 835 | + protocol: SmartProtocol, *, discovery_info: dict[str, Any] | None, batch_size: int | ||
| 836 | + ) -> list[FixtureResult]: | ||
| 837 | 837 | """Get fixture for new TAPO style protocol.""" | |
| 838 | 838 | if isinstance(protocol, SmartCameraProtocol): | |
| 839 | 839 | test_calls, successes = await get_smart_camera_test_calls(protocol) | |
@@ -964,23 +964,17 @@ async def get_smart_fixtures( | |||
| 964 | 964 | ||
| 965 | 965 | if "get_device_info" in final: | |
| 966 | 966 | # smart protocol | |
| 967 | - hw_version = final["get_device_info"]["hw_ver"] | ||
| 968 | - sw_version = final["get_device_info"]["fw_ver"] | ||
| 969 | - if discovery_info: | ||
| 970 | - model = discovery_info["device_model"] | ||
| 971 | - else: | ||
| 972 | - model = final["get_device_info"]["model"] + "(XX)" | ||
| 973 | - sw_version = sw_version.split(" ", maxsplit=1)[0] | ||
| 967 | + model_info = SmartDevice._get_device_info(final, discovery_info) | ||
| 974 | 968 | copy_folder = SMART_FOLDER | |
| 975 | 969 | else: | |
| 976 | 970 | # smart camera protocol | |
| 977 | 971 | model_info = SmartCamera._get_device_info(final, discovery_info) | |
| 978 | - model = model_info.long_name | ||
| 979 | - hw_version = model_info.hardware_version | ||
| 980 | - sw_version = model_info.firmare_version | ||
| 981 | - if model_info.region is not None: | ||
| 982 | - model = f"{model}({model_info.region})" | ||
| 983 | 972 | copy_folder = SMARTCAMERA_FOLDER | |
| 973 | + hw_version = model_info.hardware_version | ||
| 974 | + sw_version = model_info.firmware_version | ||
| 975 | + model = model_info.long_name | ||
| 976 | + if model_info.region is not None: | ||
| 977 | + model = f"{model}({model_info.region})" | ||
| 984 | 978 | ||
| 985 | 979 | save_filename = f"{model}_{hw_version}_{sw_version}.json" | |
| 986 | 980 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,23 +1,25 @@ | |||
| 1 | 1 | #!/usr/bin/env python | |
| 2 | 2 | """Script that checks supported devices and updates README.md and SUPPORTED.md.""" | |
| 3 | 3 | ||
| 4 | + from __future__ import annotations | ||
| 5 | + | ||
| 4 | 6 | import json | |
| 5 | 7 | import os | |
| 6 | 8 | import sys | |
| 7 | 9 | from pathlib import Path | |
| 8 | 10 | from string import Template | |
| 9 | - from typing import NamedTuple | ||
| 11 | + from typing import Any, NamedTuple | ||
| 10 | 12 | ||
| 11 | - from kasa.device_factory import _get_device_type_from_sys_info | ||
| 12 | 13 | from kasa.device_type import DeviceType | |
| 14 | + from kasa.iot import IotDevice | ||
| 13 | 15 | from kasa.smart import SmartDevice | |
| 14 | 16 | from kasa.smartcamera import SmartCamera | |
| 15 | 17 | ||
| 16 | 18 | ||
| 17 | 19 | class SupportedVersion(NamedTuple): | |
| 18 | 20 | """Supported version.""" | |
| 19 | 21 | ||
| 20 | - region: str | ||
| 22 | + region: str | None | ||
| 21 | 23 | hw: str | |
| 22 | 24 | fw: str | |
| 23 | 25 | auth: bool | |
@@ -45,6 +47,7 @@ class SupportedVersion(NamedTuple): | |||
| 45 | 47 | ||
| 46 | 48 | IOT_FOLDER = "tests/fixtures/" | |
| 47 | 49 | SMART_FOLDER = "tests/fixtures/smart/" | |
| 50 | + SMART_CHILD_FOLDER = "tests/fixtures/smart/child" | ||
| 48 | 51 | SMARTCAMERA_FOLDER = "tests/fixtures/smartcamera/" | |
| 49 | 52 | ||
| 50 | 53 | ||
@@ -59,9 +62,10 @@ def generate_supported(args): | |||
| 59 | 62 | ||
| 60 | 63 | supported = {"kasa": {}, "tapo": {}} | |
| 61 | 64 | ||
| 62 | - _get_iot_supported(supported) | ||
| 63 | - _get_smart_supported(supported) | ||
| 64 | - _get_smartcamera_supported(supported) | ||
| 65 | + _get_supported_devices(supported, IOT_FOLDER, IotDevice) | ||
| 66 | + _get_supported_devices(supported, SMART_FOLDER, SmartDevice) | ||
| 67 | + _get_supported_devices(supported, SMART_CHILD_FOLDER, SmartDevice) | ||
| 68 | + _get_supported_devices(supported, SMARTCAMERA_FOLDER, SmartCamera) | ||
| 65 | 69 | ||
| 66 | 70 | readme_updated = _update_supported_file( | |
| 67 | 71 | README_FILENAME, _supported_summary(supported), print_diffs | |
@@ -201,49 +205,16 @@ def _supported_text( | |||
| 201 | 205 | return brands | |
| 202 | 206 | ||
| 203 | 207 | ||
| 204 | - def _get_smart_supported(supported): | ||
| 205 | - for file in Path(SMART_FOLDER).glob("**/*.json"): | ||
| 206 | - with file.open() as f: | ||
| 207 | - fixture_data = json.load(f) | ||
| 208 | - | ||
| 209 | - if "discovery_result" in fixture_data: | ||
| 210 | - model, _, region = fixture_data["discovery_result"][ | ||
| 211 | - "device_model" | ||
| 212 | - ].partition("(") | ||
| 213 | - device_type = fixture_data["discovery_result"]["device_type"] | ||
| 214 | - else: # child devices of hubs do not have discovery result | ||
| 215 | - model = fixture_data["get_device_info"]["model"] | ||
| 216 | - region = fixture_data["get_device_info"].get("specs") | ||
| 217 | - device_type = fixture_data["get_device_info"]["type"] | ||
| 218 | - # P100 doesn't have region HW | ||
| 219 | - region = region.replace(")", "") if region else "" | ||
| 220 | - | ||
| 221 | - _protocol, devicetype = device_type.split(".") | ||
| 222 | - brand = devicetype[:4].lower() | ||
| 223 | - components = [ | ||
| 224 | - component["id"] | ||
| 225 | - for component in fixture_data["component_nego"]["component_list"] | ||
| 226 | - ] | ||
| 227 | - dt = SmartDevice._get_device_type_from_components(components, device_type) | ||
| 228 | - supported_type = DEVICE_TYPE_TO_PRODUCT_GROUP[dt] | ||
| 229 | - | ||
| 230 | - hw_version = fixture_data["get_device_info"]["hw_ver"] | ||
| 231 | - fw_version = fixture_data["get_device_info"]["fw_ver"] | ||
| 232 | - fw_version = fw_version.split(" ", maxsplit=1)[0] | ||
| 233 | - | ||
| 234 | - stype = supported[brand].setdefault(supported_type, {}) | ||
| 235 | - smodel = stype.setdefault(model, []) | ||
| 236 | - smodel.append( | ||
| 237 | - SupportedVersion(region=region, hw=hw_version, fw=fw_version, auth=True) | ||
| 238 | - ) | ||
| 239 | - | ||
| 240 | - | ||
| 241 | - def _get_smartcamera_supported(supported): | ||
| 242 | - for file in Path(SMARTCAMERA_FOLDER).glob("**/*.json"): | ||
| 208 | + def _get_supported_devices( | ||
| 209 | + supported: dict[str, Any], | ||
| 210 | + fixture_location: str, | ||
| 211 | + device_cls: type[IotDevice | SmartDevice | SmartCamera], | ||
| 212 | + ): | ||
| 213 | + for file in Path(fixture_location).glob("*.json"): | ||
| 243 | 214 | with file.open() as f: | |
| 244 | 215 | fixture_data = json.load(f) | |
| 245 | 216 | ||
| 246 | - model_info = SmartCamera._get_device_info( | ||
| 217 | + model_info = device_cls._get_device_info( | ||
| 247 | 218 | fixture_data, fixture_data.get("discovery_result") | |
| 248 | 219 | ) | |
| 249 | 220 | ||
@@ -255,30 +226,12 @@ def _get_smartcamera_supported(supported): | |||
| 255 | 226 | SupportedVersion( | |
| 256 | 227 | region=model_info.region, | |
| 257 | 228 | hw=model_info.hardware_version, | |
| 258 | - fw=model_info.firmare_version, | ||
| 229 | + fw=model_info.firmware_version, | ||
| 259 | 230 | auth=model_info.requires_auth, | |
| 260 | 231 | ) | |
| 261 | 232 | ) | |
| 262 | 233 | ||
| 263 | 234 | ||
| 264 | - def _get_iot_supported(supported): | ||
| 265 | - for file in Path(IOT_FOLDER).glob("*.json"): | ||
| 266 | - with file.open() as f: | ||
| 267 | - fixture_data = json.load(f) | ||
| 268 | - sysinfo = fixture_data["system"]["get_sysinfo"] | ||
| 269 | - dt = _get_device_type_from_sys_info(fixture_data) | ||
| 270 | - supported_type = DEVICE_TYPE_TO_PRODUCT_GROUP[dt] | ||
| 271 | - | ||
| 272 | - model, _, region = sysinfo["model"][:-1].partition("(") | ||
| 273 | - auth = "discovery_result" in fixture_data | ||
| 274 | - stype = supported["kasa"].setdefault(supported_type, {}) | ||
| 275 | - smodel = stype.setdefault(model, []) | ||
| 276 | - fw = sysinfo["sw_ver"].split(" ", maxsplit=1)[0] | ||
| 277 | - smodel.append( | ||
| 278 | - SupportedVersion(region=region, hw=sysinfo["hw_ver"], fw=fw, auth=auth) | ||
| 279 | - ) | ||
| 280 | - | ||
| 281 | - | ||
| 282 | 235 | def main(): | |
| 283 | 236 | """Entry point to module.""" | |
| 284 | 237 | generate_supported(sys.argv[1:]) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -162,7 +162,7 @@ class _DeviceInfo: | |||
| 162 | 162 | device_family: str | |
| 163 | 163 | device_type: DeviceType | |
| 164 | 164 | hardware_version: str | |
| 165 | - firmare_version: str | ||
| 165 | + firmware_version: str | ||
| 166 | 166 | firmware_build: str | |
| 167 | 167 | requires_auth: bool | |
| 168 | 168 | region: str | None | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -128,34 +128,6 @@ def _perf_log(has_params: bool, perf_type: str) -> None: | |||
| 128 | 128 | ) | |
| 129 | 129 | ||
| 130 | 130 | ||
| 131 | - def _get_device_type_from_sys_info(info: dict[str, Any]) -> DeviceType: | ||
| 132 | - """Find SmartDevice subclass for device described by passed data.""" | ||
| 133 | - if "system" not in info or "get_sysinfo" not in info["system"]: | ||
| 134 | - raise KasaException("No 'system' or 'get_sysinfo' in response") | ||
| 135 | - | ||
| 136 | - sysinfo: dict[str, Any] = info["system"]["get_sysinfo"] | ||
| 137 | - type_: str | None = sysinfo.get("type", sysinfo.get("mic_type")) | ||
| 138 | - if type_ is None: | ||
| 139 | - raise KasaException("Unable to find the device type field!") | ||
| 140 | - | ||
| 141 | - if "dev_name" in sysinfo and "Dimmer" in sysinfo["dev_name"]: | ||
| 142 | - return DeviceType.Dimmer | ||
| 143 | - | ||
| 144 | - if "smartplug" in type_.lower(): | ||
| 145 | - if "children" in sysinfo: | ||
| 146 | - return DeviceType.Strip | ||
| 147 | - if (dev_name := sysinfo.get("dev_name")) and "light" in dev_name.lower(): | ||
| 148 | - return DeviceType.WallSwitch | ||
| 149 | - return DeviceType.Plug | ||
| 150 | - | ||
| 151 | - if "smartbulb" in type_.lower(): | ||
| 152 | - if "length" in sysinfo: # strips have length | ||
| 153 | - return DeviceType.LightStrip | ||
| 154 | - | ||
| 155 | - return DeviceType.Bulb | ||
| 156 | - raise UnsupportedDeviceError(f"Unknown device type: {type_}") | ||
| 157 | - | ||
| 158 | - | ||
| 159 | 131 | def get_device_class_from_sys_info(sysinfo: dict[str, Any]) -> type[IotDevice]: | |
| 160 | 132 | """Find SmartDevice subclass for device described by passed data.""" | |
| 161 | 133 | TYPE_TO_CLASS = { | |
@@ -166,7 +138,7 @@ def get_device_class_from_sys_info(sysinfo: dict[str, Any]) -> type[IotDevice]: | |||
| 166 | 138 | DeviceType.WallSwitch: IotWallSwitch, | |
| 167 | 139 | DeviceType.LightStrip: IotLightStrip, | |
| 168 | 140 | } | |
| 169 | - return TYPE_TO_CLASS[_get_device_type_from_sys_info(sysinfo)] | ||
| 141 | + return TYPE_TO_CLASS[IotDevice._get_device_type_from_sys_info(sysinfo)] | ||
| 170 | 142 | ||
| 171 | 143 | ||
| 172 | 144 | def get_device_class_from_family( | |
| Back | FazBrowse Home | New Git URL |
0 commit comments