diff --git a/README.md b/README.md index b99970bb..58f3c826 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,10 @@ The following devices have been tested and confirmed as working. If your device > [!NOTE] > The hub attached Tapo buttons S200B and S200D do not currently support alerting when the button is pressed. +> [!NOTE] +> Some firmware versions of Tapo Cameras will not authenticate unless you enable "Tapo Lab" > "Third-Party Compatibility" in the native Tapo app. +> Alternatively, you can factory reset and then prevent the device from accessing the internet. + ### Supported Kasa devices diff --git a/SUPPORTED.md b/SUPPORTED.md index ba7726cc..d3c1f1e6 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -5,6 +5,9 @@ The following devices have been tested and confirmed as working. If your device > [!NOTE] > The hub attached Tapo buttons S200B and S200D do not currently support alerting when the button is pressed. +> [!NOTE] +> Some firmware versions of Tapo Cameras will not authenticate unless you enable "Tapo Lab" > "Third-Party Compatibility" in the native Tapo app. +> Alternatively, you can factory reset and then prevent the device from accessing the internet. diff --git a/kasa/protocols/smartprotocol.py b/kasa/protocols/smartprotocol.py index e187785d..7f02b45e 100644 --- a/kasa/protocols/smartprotocol.py +++ b/kasa/protocols/smartprotocol.py @@ -185,18 +185,18 @@ class SmartProtocol(BaseProtocol): multi_result: dict[str, Any] = {} smart_method = "multipleRequest" + end = len(requests) + # The SmartCamProtocol sends requests with a length 1 as a + # multipleRequest. The SmartProtocol doesn't so will never + # raise_on_error + raise_on_error = end == 1 + multi_requests = [ {"method": method, "params": params} if params else {"method": method} for method, params in requests.items() if method not in FORCE_SINGLE_REQUEST ] - end = len(multi_requests) - # The SmartCamProtocol sends requests with a length 1 as a - # multipleRequest. The SmartProtocol doesn't so will never - # raise_on_error - raise_on_error = end == 1 - # Break the requests down as there can be a size limit step = self._multi_request_batch_size if step == 1: @@ -287,7 +287,9 @@ class SmartProtocol(BaseProtocol): resp = await self._transport.send( self.get_smart_request(method, params) ) - self._handle_response_error_code(resp, method, raise_on_error=False) + self._handle_response_error_code( + resp, method, raise_on_error=raise_on_error + ) multi_result[method] = resp.get("result") return multi_result diff --git a/tests/fakeprotocol_smartcam.py b/tests/fakeprotocol_smartcam.py index e8cc6f30..381a0a89 100644 --- a/tests/fakeprotocol_smartcam.py +++ b/tests/fakeprotocol_smartcam.py @@ -221,35 +221,38 @@ class FakeSmartCamTransport(BaseTransport): return {**result, "error_code": 0} else: return {"error_code": -1} - elif method[:3] == "get": + + if method in info: params = request_dict.get("params") - if method in info: - result = copy.deepcopy(info[method]) - if "start_index" in result and "sum" in result: - list_key = next( - iter([key for key in result if isinstance(result[key], list)]) - ) - start_index = ( - start_index - if (params and (start_index := params.get("start_index"))) - else 0 - ) + result = copy.deepcopy(info[method]) + if "start_index" in result and "sum" in result: + list_key = next( + iter([key for key in result if isinstance(result[key], list)]) + ) + start_index = ( + start_index + if (params and (start_index := params.get("start_index"))) + else 0 + ) - result[list_key] = result[list_key][ - start_index : start_index + self.list_return_size - ] - return {"result": result, "error_code": 0} - if ( - # FIXTURE_MISSING is for service calls not in place when - # SMART fixtures started to be generated - missing_result := self.FIXTURE_MISSING_MAP.get(method) - ) and missing_result[0] in self.components: - # Copy to info so it will work with update methods - info[method] = copy.deepcopy(missing_result[1]) - result = copy.deepcopy(info[method]) - return {"result": result, "error_code": 0} + result[list_key] = result[list_key][ + start_index : start_index + self.list_return_size + ] + return {"result": result, "error_code": 0} + if self.verbatim: return {"error_code": -1} + + if ( + # FIXTURE_MISSING is for service calls not in place when + # SMART fixtures started to be generated + missing_result := self.FIXTURE_MISSING_MAP.get(method) + ) and missing_result[0] in self.components: + # Copy to info so it will work with update methods + info[method] = copy.deepcopy(missing_result[1]) + result = copy.deepcopy(info[method]) + return {"result": result, "error_code": 0} + return {"error_code": -1} async def close(self) -> None: diff --git a/tests/fixtures/smartcam/C210(EU)_2.0_1.4.3.json b/tests/fixtures/smartcam/C210(EU)_2.0_1.4.3.json index b6280118..d4de5b9f 100644 --- a/tests/fixtures/smartcam/C210(EU)_2.0_1.4.3.json +++ b/tests/fixtures/smartcam/C210(EU)_2.0_1.4.3.json @@ -7,8 +7,8 @@ "connect_type": "wireless", "device_id": "0000000000000000000000000000000000000000", "http_port": 443, - "last_alarm_time": "0", - "last_alarm_type": "", + "last_alarm_time": "1733422805", + "last_alarm_type": "motion", "owner": "00000000000000000000000000000000", "sd_status": "offline" }, @@ -32,7 +32,8 @@ "mac": "40-AE-30-00-00-00", "mgt_encrypt_schm": { "is_support_https": true - } + }, + "protocol_version": 1 } }, "getAlertConfig": { @@ -266,15 +267,22 @@ "getClockStatus": { "system": { "clock_status": { - "local_time": "2024-11-01 13:58:50", - "seconds_from_1970": 1730469530 + "local_time": "2024-12-15 11:28:40", + "seconds_from_1970": 1734262120 + } + } + }, + "getConnectStatus": { + "onboarding": { + "get_connect_status": { + "status": 0 } } }, "getConnectionType": { "link_type": "wifi", "rssi": "3", - "rssiValue": -57, + "rssiValue": -61, "ssid": "I01BU0tFRF9TU0lEIw==" }, "getDetectionConfig": { @@ -321,7 +329,7 @@ "getFirmwareAutoUpgradeConfig": { "auto_upgrade": { "common": { - "enabled": "on", + "enabled": "off", "random_range": "120", "time": "03:00" } @@ -338,8 +346,8 @@ "getLastAlarmInfo": { "system": { "last_alarm_info": { - "last_alarm_time": "0", - "last_alarm_type": "" + "last_alarm_time": "1733422805", + "last_alarm_type": "motion" } } }, @@ -961,5 +969,35 @@ } } } + }, + "scanApList": { + "onboarding": { + "scan": { + "ap_list": [ + { + "auth": 4, + "bssid": "000000000000", + "encryption": 3, + "rssi": 2, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 4, + "bssid": "000000000000", + "encryption": 3, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 4, + "bssid": "000000000000", + "encryption": 3, + "rssi": 0, + "ssid": "I01BU0tFRF9TU0lEIw==" + } + ], + "wpa3_supported": "false" + } + } } } diff --git a/tests/fixtures/smartcam/H200(EU)_1.0_1.3.2.json b/tests/fixtures/smartcam/H200(EU)_1.0_1.3.2.json index 9ccaa7e0..4ef99fae 100644 --- a/tests/fixtures/smartcam/H200(EU)_1.0_1.3.2.json +++ b/tests/fixtures/smartcam/H200(EU)_1.0_1.3.2.json @@ -26,6 +26,7 @@ "firmware_version": "1.3.2 Build 20240424 rel.75425", "hardware_version": "1.0", "ip": "127.0.0.123", + "isResetWiFi": false, "is_support_iot_cloud": true, "mac": "A8-6E-84-00-00-00", "mgt_encrypt_schm": { @@ -214,8 +215,8 @@ "fw_ver": "1.11.0 Build 230821 Rel.113553", "hw_id": "00000000000000000000000000000000", "hw_ver": "1.0", - "jamming_rssi": -108, - "jamming_signal_level": 2, + "jamming_rssi": -119, + "jamming_signal_level": 1, "lastOnboardingTimestamp": 1714016798, "mac": "202351000000", "model": "S200B", @@ -224,7 +225,7 @@ "parent_device_id": "0000000000000000000000000000000000000000", "region": "Europe/London", "report_interval": 16, - "rssi": -66, + "rssi": -60, "signal_level": 3, "specs": "EU", "status": "online", @@ -245,8 +246,17 @@ "getClockStatus": { "system": { "clock_status": { - "local_time": "2024-11-01 13:56:27", - "seconds_from_1970": 1730469387 + "local_time": "1984-10-21 23:48:23", + "seconds_from_1970": 467246903 + } + } + }, + "getConnectStatus": { + "onboarding": { + "get_connect_status": { + "current_ssid": "", + "err_code": 0, + "status": 0 } } }, @@ -329,6 +339,10 @@ } } }, + "getMatterSetupInfo": { + "setup_code": "00000000000", + "setup_payload": "00:000000-000000000000" + }, "getMediaEncrypt": { "cet": { "media_encrypt": { @@ -353,7 +367,7 @@ "getSirenConfig": { "duration": 300, "siren_type": "Doorbell Ring 1", - "volume": "6" + "volume": "1" }, "getSirenStatus": { "status": "off", @@ -389,5 +403,98 @@ "zone_id": "Europe/London" } } + }, + "scanApList": { + "onboarding": { + "scan": { + "ap_list": [ + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 2, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 0, + "bssid": "000000000000", + "encryption": 0, + "rssi": 2, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 3, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 3, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 4, + "bssid": "000000000000", + "encryption": 3, + "rssi": 2, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + }, + { + "auth": 3, + "bssid": "000000000000", + "encryption": 2, + "rssi": 1, + "ssid": "I01BU0tFRF9TU0lEIw==" + } + ], + "wpa3_supported": "false" + } + } } }