python-kasa icon indicating copy to clipboard operation
python-kasa copied to clipboard

Tapo P110M Authenticate Failure

Open gebjeff opened this issue 1 year ago • 15 comments

I have a Tapo H200 and two Tapo P110M plugs. All three devices have been added to the Tapo app and the H200 and one P110M were successfully integrated with HA (i.e., successfully authenticates to tplinkcloud.com). The second P110M fails to authenticate even though I'm using the same account name and password.

I've taken the following actions:

  1. Performed several factory resets to the problematic P110M.
  2. Deleted and successfully readded both P110Ms to the Tapo app.
  3. Deleted and successfully readded the H200 and the working P110M to the HA integration.
  4. Unsuccessfully tried to add the problematic P110M several times and authentication failed both times.

I've attached the debug log. debug log.txt

gebjeff avatar Dec 09 '24 00:12 gebjeff

Could you try rebooting the device to see if that helps? Some users have reported that a reboot (or removing and replugging the device) has suddenly made their devices work.

rytilahti avatar Dec 09 '24 23:12 rytilahti

Thanks for your quick response. I unplugged it and then plugged it back in. I tried adding it, having the integration search the whole subnet. The integration found the plug, I selected it, entered my authentication info, and it failed, with the message “ Invalid authentication: Server response doesn't match our challenge on ip 192.168.x.x”.Any additional thoughts or info I can provide?Regards, Jeff Edelheit12518 Chasbarb TerraceOak Hill, VA 20171-2468(703) 758-0241 (h)(703) 626-4605 (c)On Dec 9, 2024, at 6:51 PM, Teemu R. @.***> wrote: Could you try rebooting the device to see if that helps? Some users have reported that a reboot (or removing and replugging the device) has suddenly made their devices work.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>

gebjeff avatar Dec 10 '24 00:12 gebjeff

This issue is also tracked on this ticket:

https://github.com/home-assistant/core/issues/138376

I own several P110M which I bought a few days ago, all run the same latest firmware and under the same account. They were all configured within the same 30min via the Tapo app. Only the very first one that I have set up allows me to authenticate successfully, all the others fail.

kasa --debug --host 192.168.xxxx --username xxx --password xxxx only works for the very first P110M device I've added to the app. For all the others I get "Server response doesn't match our challenge on ip".

I have also tried to remove one of the devices from the app, even reset it to factory default (long press the power button) - done several times. Nothing works. The above command will only be able to successfully authenticate against the only P110M in my account that works, but not the others.

Edit:

One difference I've noticed is that the one that accepts the auth states 'obd_src': 'tplink' while all the others which are not working have 'obd_src': 'tss' (information displayed when running --debug.

Thireus avatar Feb 25 '25 11:02 Thireus

According to the Tapo Android app decompiled code, there is some evidence that there is a special treatment for "tss" (TP-Link Simple Setup) Onboarding Source devices.

        boolean z11 = "amazon_atr".equals(obdSrc) || "tss".equals(obdSrc);
        boolean z12 = tDPIoTDevice.getEncryptType() == EnumEncryptType.KLAP;
        if ((z11 || z12) && tCAccountBean.getLocalUserName() != null) {
            h11 = hi0.a.h(hi0.a.h(tCAccountBean.getLocalUserName()) + "_" + com.tplink.libbasenetwork.utils.h.a(tDPIoTDevice.getMac()));
        } else {
            h11 = tCAccountBean.getLocalUserName() + "_" + com.tplink.libbasenetwork.utils.h.a(tDPIoTDevice.getMac());
        }

and

    public static byte[] g(byte[] bArr) throws NoSuchAlgorithmException {
        MessageDigest messageDigest = MessageDigest.getInstance("md5");
        messageDigest.update(bArr);
        return messageDigest.digest();
    }

    public static String h(String str) {
        try {
            return l(g(str.getBytes()));
        } catch (NoSuchAlgorithmException e11) {
            yh0.a.e(e11.toString());
            return str;
        }
    }

...

    public static String l(byte[] bArr) {
        StringBuilder sb2 = new StringBuilder();
        for (byte b11 : bArr) {
            String hexString = Integer.toHexString(b11 & 255);
            if (hexString.length() == 1) {
                sb2.append(AutoSwitchDate.DISABLE);
                sb2.append(hexString);
            } else {
                sb2.append(hexString);
            }
        }
        return sb2.toString();
    }

This is the relevant code.

Edit: Not sure if/where this is used at this stage... or if this even related to how the client hash gets computed

Thireus avatar Feb 25 '25 15:02 Thireus

@Thireus is this fixable I havent a clue. Trying to prevent myself from going to another device.

barney34 avatar Feb 25 '25 16:02 barney34

@barney34, yes this is fixable. Need to reverse the device firmware (Tapo apps don't appear to use the local KLAP protocol... they communicate with the cloud via TLS) and implement the same algorithm that computes the hash for these "tss" devices. Can you confirm your device also shows "tss" when you --debug?

This is the line of code that should trigger:

https://github.com/python-kasa/python-kasa/blob/f0abc2800dc7127034713e5ac2862592f536ba2b/kasa/transports/klaptransport.py#L221-L223

But it doesn't because the local_seed_auth_hash is different from the server_hash. Which to me would suggest the local_seed_auth_hash may be computed incorrectly of these "tss" devices. My guess is that the credentials need to be in a different format.

Thireus avatar Feb 25 '25 17:02 Thireus

@Thireus I dont know how to do that but if you tell me I can figure it out

barney34 avatar Feb 25 '25 17:02 barney34

@barney34

pip install python-kasa
kasa --debug --host 192.168.xxxx --username 'xxxxxx' --password 'xxxxxx'

Where host is you device's IP, username and password are your tapo account credentials.

Thireus avatar Feb 25 '25 18:02 Thireus

@Thireus when my work I get a full dump when mine goes offline the IP I can still ping but cant not run the debug against the device it in some terrible state. I have to reboot it to get it back even the local power and or dimmers wont work but the IP I can ping

Discovering device 192.168.100.125 for 10 seconds

DEBUG [DISCOVERY] 192.168.100.125 >> {'system': {'get_sysinfo': {}}} discover.py:326 DEBUG Waiting a total of 10 seconds for responses... discover.py:587 DEBUG Using IotPlug for IOT.SMARTPLUGSWITCH device_factory.py:179 DEBUG Finding protocol for 192.168.100.125 device_factory.py:194 DEBUG Finding protocol for DeviceFamily.IotSmartPlugSwitch device_factory.py:197 DEBUG Finding transport for IOT.KLAP device_factory.py:226 DEBUG Created KLAP transport for 192.168.100.125 klaptransport.py:141 DEBUG [DISCOVERY] 192.168.100.125 << {'error_code': 0, discover.py:924 'result': {'device_id': 'REDACTED_0387f481d44611a9ec9034b',
'device_model': 'ES20M(US)',
'device_type': 'IOT.SMARTPLUGSWITCH',
'factory_default': False,
'hw_ver': '1.0',
'ip': '192.168.100.125',
'mac': '40-ED-00-00-00-00',
'mgt_encrypt_schm': {'ANS': False,
'encrypt_type': 'KLAP',
'http_port': 80,
'is_support_https': False,
'lv': 2,
'new_klap': 1},
'obd_src': 'tplink',
'owner': 'REDACTED_678F6E96F0E783E0EA8977B',
'protocol_version': 1}}
DEBUG Initializing 192.168.100.125 of type <class 'kasa.iot.iotplug.IotPlug'> device.py:212 DEBUG Performing the initial update to obtain sysinfo iotdevice.py:314 DEBUG 192.168.100.125 >> {"system":{"get_sysinfo":{}}} iotprotocol.py:143 DEBUG Starting handshake with 192.168.100.125 klaptransport.py:317 DEBUG Posting to http://192.168.100.125/app/handshake1 httpclient.py:88 DEBUG Device 192.168.100.125 received an os error, enabling sequential request delay: [Errno 104] Connection reset by peer httpclient.py:136 DEBUG 192.168.100.125 >> {"system":{"get_sysinfo":{}}} iotprotocol.py:143 DEBUG Starting handshake with 192.168.100.125 klaptransport.py:317 DEBUG Device 192.168.100.125 waiting 0.24917759899994962 seconds to send request httpclient.py:81 DEBUG Posting to http://192.168.100.125/app/handshake1 httpclient.py:88 DEBUG 192.168.100.125 >> {"system":{"get_sysinfo":{}}} iotprotocol.py:143 DEBUG Starting handshake with 192.168.100.125 klaptransport.py:317 DEBUG Device 192.168.100.125 waiting 0.24822860599988417 seconds to send request httpclient.py:81 DEBUG Posting to http://192.168.100.125/app/handshake1 httpclient.py:88 DEBUG 192.168.100.125 >> {"system":{"get_sysinfo":{}}} iotprotocol.py:143 DEBUG Starting handshake with 192.168.100.125 klaptransport.py:317 DEBUG Device 192.168.100.125 waiting 0.2486628260000998 seconds to send request httpclient.py:81 DEBUG Posting to http://192.168.100.125/app/handshake1 httpclient.py:88 DEBUG Giving up on 192.168.100.125 after 3 retries iotprotocol.py:91 Raised error: ('Device connection error: 192.168.100.125: [Errno 104] Connection reset by peer', ClientOSError(104, 'Connection reset by peer'))

barney34 avatar Feb 25 '25 19:02 barney34

@barney34, I could be wrong but it looks like to me that you may have some firewall on your network blocking http communications to the device... try to access http://192.168.100.125/ to see if that works.

Thireus avatar Feb 25 '25 21:02 Thireus

@Thireus it works in about 30 seconds when the device comes back on line. I can ping the device the whole time. I dont want to derail the conversation.

Discovering device 192.168.100.125 for 10 seconds DEBUG [DISCOVERY] 192.168.100.125 >> {'system': {'get_sysinfo': {}}} discover.py:326 DEBUG Waiting a total of 10 seconds for responses... discover.py:587 DEBUG [DISCOVERY] 192.168.100.125 << {'system': {'get_sysinfo': {'active_mode': 'none', discover.py:740 'alias': '#MASKED_NAME#',
'brightness': 52,
'dev_name': 'Wi-Fi Smart Dimmer with sensor',
'deviceId': 'REDACTED_3547B7DCA5429DC92708C762180D397',
'err_code': 0,
'feature': 'TIM',
'hold_on': 1,
'hwId': 'REDACTED_F31AF2EA08CD6610E699746',
'hw_ver': '1.0',
'icon_hash': '',
'latitude_i': 0,
'led_off': 0,
'lnk_on': 1,
'longitude_i': 0,
'mac': '40:ED:00:00:00:00',
'max_thr': 1,
'mic_type': 'IOT.SMARTPLUGSWITCH',
'model': 'ES20M(US)',
'next_action': {'type': -1},
'obd_src': 'tplink',
'oemId': 'REDACTED_BABAA04B18C0033D0368A92',
'on_time': 0,
'preferred_state': [{'brightness': 97, 'index': 0},
{'brightness': 75, 'index': 1},
{'brightness': 50, 'index': 2},
{'brightness': 25, 'index': 3}],
'relay_state': 0,
'rssi': -50,
'status': 'new',
'sw_ver': '1.1.5 Build 241206 Rel.142818',
'updating': 0}}}
DEBUG Initializing 192.168.100.125 of type <class 'kasa.iot.iotdimmer.IotDimmer'> device.py:212 DEBUG Finding protocol for 192.168.100.125 device_factory.py:194 DEBUG Finding protocol for DeviceFamily.IotSmartPlugSwitch device_factory.py:197 DEBUG Finding transport for IOT.XOR device_factory.py:226 DEBUG Adding module <Module Schedule (schedule) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Usage (schedule) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Antitheft (anti_theft) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Time (time) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Cloud (cnCloud) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Led (system) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Motion (smartlife.iot.PIR) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module AmbientLight (smartlife.iot.LAS) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Dimmer (smartlife.iot.dimmer) for 192.168.100.125> iotdevice.py:221 DEBUG Adding module <Module Light (light) for 192.168.100.125> iotdevice.py:221 DEBUG Initial update, so consider supported: schedule iotmodule.py:73 DEBUG Adding query for <Module Schedule (schedule) for 192.168.100.125>: {'schedule': {'get_rules': {}, 'get_next_action': {}}} iotdevice.py:411 DEBUG Initial update, so consider supported: schedule iotmodule.py:73 DEBUG Adding query for <Module Usage (schedule) for 192.168.100.125>: {'schedule': {'get_realtime': {}, 'get_daystat': {'year': 2025, iotdevice.py:411 'month': 2}, 'get_monthstat': {'year': 2025}}}
DEBUG Initial update, so consider supported: anti_theft iotmodule.py:73 DEBUG Adding query for <Module Antitheft (anti_theft) for 192.168.100.125>: {'anti_theft': {'get_rules': {}, 'get_next_action': {}}} iotdevice.py:411 DEBUG Initial update, so consider supported: time iotmodule.py:73 DEBUG Adding query for <Module Time (time) for 192.168.100.125>: {'time': {'get_time': {}, 'get_timezone': {}}} iotdevice.py:411 DEBUG Initial update, so consider supported: cnCloud iotmodule.py:73 DEBUG Adding query for <Module Cloud (cnCloud) for 192.168.100.125>: {'cnCloud': {'get_info': {}}} iotdevice.py:411 DEBUG Adding query for <Module Led (system) for 192.168.100.125>: {} iotdevice.py:411 DEBUG Initial update, so consider supported: smartlife.iot.PIR iotmodule.py:73 DEBUG Adding query for <Module Motion (smartlife.iot.PIR) for 192.168.100.125>: {'smartlife.iot.PIR': {'get_config': {}, iotdevice.py:411 'get_adc_value': {}}}
DEBUG Initial update, so consider supported: smartlife.iot.LAS iotmodule.py:73 DEBUG Adding query for <Module AmbientLight (smartlife.iot.LAS) for 192.168.100.125>: {'smartlife.iot.LAS': {'get_config': {}, iotdevice.py:411 'get_current_brt': {}}}
DEBUG Initial update, so consider supported: smartlife.iot.dimmer iotmodule.py:73 DEBUG Adding query for <Module Dimmer (smartlife.iot.dimmer) for 192.168.100.125>: {'smartlife.iot.dimmer': {'get_dimmer_parameters': iotdevice.py:411 {}, 'get_default_behavior': {}}}
DEBUG Initial update, so consider supported: light iotmodule.py:73 DEBUG Adding query for <Module Light (light) for 192.168.100.125>: {} iotdevice.py:411 DEBUG 192.168.100.125 >> iotprotocol.py:143 {"system":{"get_sysinfo":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2025,"month
":2},"get_monthstat":{"year":2025}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},
"cnCloud":{"get_info":{}},"smartlife.iot.PIR":{"get_config":{},"get_adc_value":{}},"smartlife.iot.LAS":{"get_config":{},"get_cur
rent_brt":{}},"smartlife.iot.dimmer":{"get_dimmer_parameters":{},"get_default_behavior":{}}}
DEBUG Device 192.168.100.125 sending query xortransport.py:79 {"system":{"get_sysinfo":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2025,"month
":2},"get_monthstat":{"year":2025}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},
"cnCloud":{"get_info":{}},"smartlife.iot.PIR":{"get_config":{},"get_adc_value":{}},"smartlife.iot.LAS":{"get_config":{},"get_cur
rent_brt":{}},"smartlife.iot.dimmer":{"get_dimmer_parameters":{},"get_default_behavior":{}}}
DEBUG Device 192.168.100.125 query response received xortransport.py:91 DEBUG 192.168.100.125 << {'anti_theft': {'get_next_action': {'err_code': -2, iotprotocol.py:152 'err_msg': 'member not support'},
'get_rules': {'enable': 0,
'err_code': 0,
'rule_list': [],
'version': 2}},
'cnCloud': {'get_info': {'binded': 1,
'cld_connection': 1,
'err_code': 0,
'fwDlPage': '',
'fwNotifyType': -1,
'illegalType': 0,
'server': 'n-devs.tplinkcloud.com',
'stopConnect': 0,
'tcspInfo': '',
'tcspStatus': 1,
'username': '[email protected]'}},
'schedule': {'get_daystat': {'day_list': [{'day': 1,
'month': 2,
'time': 107,
'year': 2025},
{'day': 2,
'month': 2,
'time': 72,
'year': 2025},
{'day': 3,
'month': 2,
'time': 100,
'year': 2025},
{'day': 4,
'month': 2,
'time': 117,
'year': 2025},
{'day': 5,
'month': 2,
'time': 130,
'year': 2025},
{'day': 6,
'month': 2,
'time': 190,
'year': 2025},
{'day': 7,
'month': 2,
'time': 113,
'year': 2025},
{'day': 8,
'month': 2,
'time': 122,
'year': 2025},
{'day': 9,
'month': 2,
'time': 163,
'year': 2025},
{'day': 10,
'month': 2,
'time': 99,
'year': 2025},
{'day': 11,
'month': 2,
'time': 104,
'year': 2025},
{'day': 12,
'month': 2,
'time': 872,
'year': 2025},
{'day': 13,
'month': 2,
'time': 126,
'year': 2025},
{'day': 14,
'month': 2,
'time': 91,
'year': 2025},
{'day': 15,
'month': 2,
'time': 103,
'year': 2025},
{'day': 16,
'month': 2,
'time': 119,
'year': 2025},
{'day': 17,
'month': 2,
'time': 365,
'year': 2025},
{'day': 18,
'month': 2,
'time': 83,
'year': 2025},
{'day': 19,
'month': 2,
'time': 137,
'year': 2025},
{'day': 20,
'month': 2,
'time': 249,
'year': 2025},
{'day': 21,
'month': 2,
'time': 479,
'year': 2025},
{'day': 22,
'month': 2,
'time': 10,
'year': 2025},
{'day': 23,
'month': 2,
'time': 93,
'year': 2025},
{'day': 24,
'month': 2,
'time': 137,
'year': 2025},
{'day': 25,
'month': 2,
'time': 67,
'year': 2025}],
'err_code': 0},
'get_monthstat': {'err_code': 0,
'month_list': [{'month': 1,
'time': 3763,
'year': 2025},
{'month': 2,
'time': 4248,
'year': 2025}]},
'get_next_action': {'err_code': 0, 'type': -1},
'get_realtime': {'err_code': -2, 'err_msg': 'member not support'},
'get_rules': {'enable': 1,
'err_code': 0,
'rule_list': [],
'version': 2}},
'smartlife.iot.LAS': {'get_config': {'devs': [{'dark_index': 0,
'enable': 1,
'hw_id': 0,
'level_array': [{'adc': 390,
'name': 'cloudy',
'value': 16},
{'adc': 300,
'name': 'overcast',
'value': 12},
{'adc': 222,
'name': 'dawn',
'value': 9},
{'adc': 222,
'name': 'twilight',
'value': 9},
{'adc': 111,
'name': 'total '
'darkness',
'value': 5},
{'adc': 2400,
'name': 'custom',
'value': 95}],
'max_adc': 2550,
'min_adc': 0}],
'err_code': 0,
'ver': '1.0'},
'get_current_brt': {'err_code': 0, 'value': 5}},
'smartlife.iot.PIR': {'get_adc_value': {'err_code': 0, 'value': 2096},
'get_config': {'array': [80, 50, 20, 0],
'cold_time': 600000,
'enable': 1,
'err_code': 0,
'max_adc': 4095,
'min_adc': 0,
'trigger_index': 1,
'version': '1.0'}},
'smartlife.iot.dimmer': {'get_default_behavior': {'double_click': {'mode': 'unknown'},
'err_code': 0,
'long_press': {'mode': 'unknown'}},
'get_dimmer_parameters': {'bulb_type': 1,
'err_code': 0,
'fadeOffTime': 0,
'fadeOnTime': 0,
'gentleOffTime': 10000,
'gentleOnTime': 3000,
'maxThreshold': 100,
'minThreshold': 14,
'rampRate': 30}},
'system': {'get_sysinfo': {'active_mode': 'none',
'alias': '#MASKED_NAME#',
'brightness': 52,
'dev_name': 'Wi-Fi Smart Dimmer with sensor',
'deviceId': 'REDACTED_3547B7DCA5429DC92708C762180D397',
'err_code': 0,
'feature': 'TIM',
'hold_on': 1,
'hwId': 'REDACTED_F31AF2EA08CD6610E699746',
'hw_ver': '1.0',
'icon_hash': '',
'latitude_i': 0,
'led_off': 0,
'lnk_on': 1,
'longitude_i': 0,
'mac': '40:ED:00:00:00:00',
'max_thr': 1,
'mic_type': 'IOT.SMARTPLUGSWITCH',
'model': 'ES20M(US)',
'next_action': {'type': -1},
'obd_src': 'tplink',
'oemId': 'REDACTED_BABAA04B18C0033D0368A92',
'on_time': 0,
'preferred_state': [{'brightness': 97, 'index': 0},
{'brightness': 75, 'index': 1},
{'brightness': 50, 'index': 2},
{'brightness': 25, 'index': 3}],
'relay_state': 0,
'rssi': -50,
'status': 'new',
'sw_ver': '1.1.5 Build 241206 Rel.142818',
'updating': 0}},
'time': {'get_time': {'err_code': 0,
'hour': 16,
'mday': 25,
'min': 59,
'month': 2,
'sec': 47,
'year': 2025},
'get_timezone': {'err_code': 0, 'index': 18}}}
DEBUG Initial update, so consider supported: light iotmodule.py:73 == Main Closet - ES20M == Host: 192.168.100.125 Port: 9999 Device state: False Time: 2025-02-25 16:59:47-05:00 (tz: EST) Hardware: 1.0 (US) Firmware: 1.1.5 Build 241206 Rel.142818 MAC (rssi): 40:ED:00:6D:80:EF (-50)

== Primary features == State (state): False PIR Triggered (pir_triggered): False Ambient Light (ambient_light): 5 % Brightness (brightness): 52 (range: 0-100)

== Information == On since (on_since): None Cloud connection (cloud_connection): True PIR Value (pir_value): -49

== Configuration == LED (led): True PIR enabled (pir_enabled): True Motion Sensor Range (pir_range): Far Mid Near Custom Motion Sensor Threshold (pir_threshold): 50 (range: 0-100) Ambient light enabled (ambient_light_enabled): True Minimum dimming level (dimmer_threshold_min): 14 (range: 0-51) Dimmer fade off time (dimmer_fade_off_time): 0:00:00 (range: 0-10000) Dimmer fade on time (dimmer_fade_on_time): 0:00:00 (range: 0-10000) Dimmer gentle off time (dimmer_gentle_off_time): 0:00:10 (range: 0-120000) Dimmer gentle on time (dimmer_gentle_on_time): 0:00:03 (range: 0-120000) Dimmer ramp rate (dimmer_ramp_rate): 30 (range: 10-50)

== Debug == RSSI (rssi): -50 dBm Reboot (reboot): <Action> PIR ADC Value (pir_adc_value): 2096 PIR ADC Min (pir_adc_min): 0 PIR ADC Mid (pir_adc_mid): 2047 PIR ADC Max (pir_adc_max): 4095 PIR Percentile (pir_percent): -2.3937469467513433 %

DEBUG Adding query for <Module Schedule (schedule) for 192.168.100.125>: {'schedule': {'get_rules': {}, 'get_next_action': {}}} iotdevice.py:411 DEBUG Adding query for <Module Usage (schedule) for 192.168.100.125>: {'schedule': {'get_realtime': {}, 'get_daystat': {'year': 2025, iotdevice.py:411 'month': 2}, 'get_monthstat': {'year': 2025}}}
DEBUG Adding query for <Module Antitheft (anti_theft) for 192.168.100.125>: {'anti_theft': {'get_rules': {}, 'get_next_action': {}}} iotdevice.py:411 DEBUG Adding query for <Module Time (time) for 192.168.100.125>: {'time': {'get_time': {}, 'get_timezone': {}}} iotdevice.py:411 DEBUG Adding query for <Module Cloud (cnCloud) for 192.168.100.125>: {'cnCloud': {'get_info': {}}} iotdevice.py:411 DEBUG Adding query for <Module Led (system) for 192.168.100.125>: {} iotdevice.py:411 DEBUG Adding query for <Module Motion (smartlife.iot.PIR) for 192.168.100.125>: {'smartlife.iot.PIR': {'get_config': {}, iotdevice.py:411 'get_adc_value': {}}}
DEBUG Adding query for <Module AmbientLight (smartlife.iot.LAS) for 192.168.100.125>: {'smartlife.iot.LAS': {'get_config': {}, iotdevice.py:411 'get_current_brt': {}}}
DEBUG Adding query for <Module Dimmer (smartlife.iot.dimmer) for 192.168.100.125>: {'smartlife.iot.dimmer': {'get_dimmer_parameters': iotdevice.py:411 {}, 'get_default_behavior': {}}}
DEBUG Initial update, so consider supported: light iotmodule.py:73 DEBUG Adding query for <Module Light (light) for 192.168.100.125>: {} iotdevice.py:411 DEBUG 192.168.100.125 >> iotprotocol.py:143 {"system":{"get_sysinfo":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2025,"month
":2},"get_monthstat":{"year":2025}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},
"cnCloud":{"get_info":{}},"smartlife.iot.PIR":{"get_config":{},"get_adc_value":{}},"smartlife.iot.LAS":{"get_config":{},"get_cur
rent_brt":{}},"smartlife.iot.dimmer":{"get_dimmer_parameters":{},"get_default_behavior":{}}}
DEBUG Device 192.168.100.125 sending query xortransport.py:79 {"system":{"get_sysinfo":{}},"schedule":{"get_rules":{},"get_next_action":{},"get_realtime":{},"get_daystat":{"year":2025,"month
":2},"get_monthstat":{"year":2025}},"anti_theft":{"get_rules":{},"get_next_action":{}},"time":{"get_time":{},"get_timezone":{}},
"cnCloud":{"get_info":{}},"smartlife.iot.PIR":{"get_config":{},"get_adc_value":{}},"smartlife.iot.LAS":{"get_config":{},"get_cur
rent_brt":{}},"smartlife.iot.dimmer":{"get_dimmer_parameters":{},"get_default_behavior":{}}}
DEBUG Device 192.168.100.125 query response received xortransport.py:91 DEBUG 192.168.100.125 << {'anti_theft': {'get_next_action': {'err_code': -2, iotprotocol.py:152 'err_msg': 'member not support'},
'get_rules': {'enable': 0,
'err_code': 0,
'rule_list': [],
'version': 2}},
'cnCloud': {'get_info': {'binded': 1,
'cld_connection': 1,
'err_code': 0,
'fwDlPage': '',
'fwNotifyType': -1,
'illegalType': 0,
'server': 'n-devs.tplinkcloud.com',
'stopConnect': 0,
'tcspInfo': '',
'tcspStatus': 1,
'username': '[email protected]'}},
'schedule': {'get_daystat': {'day_list': [{'day': 1,
'month': 2,
'time': 107,
'year': 2025},
{'day': 2,
'month': 2,
'time': 72,
'year': 2025},
{'day': 3,
'month': 2,
'time': 100,
'year': 2025},
{'day': 4,
'month': 2,
'time': 117,
'year': 2025},
{'day': 5,
'month': 2,
'time': 130,
'year': 2025},
{'day': 6,
'month': 2,
'time': 190,
'year': 2025},
{'day': 7,
'month': 2,
'time': 113,
'year': 2025},
{'day': 8,
'month': 2,
'time': 122,
'year': 2025},
{'day': 9,
'month': 2,
'time': 163,
'year': 2025},
{'day': 10,
'month': 2,
'time': 99,
'year': 2025},
{'day': 11,
'month': 2,
'time': 104,
'year': 2025},
{'day': 12,
'month': 2,
'time': 872,
'year': 2025},
{'day': 13,
'month': 2,
'time': 126,
'year': 2025},
{'day': 14,
'month': 2,
'time': 91,
'year': 2025},
{'day': 15,
'month': 2,
'time': 103,
'year': 2025},
{'day': 16,
'month': 2,
'time': 119,
'year': 2025},
{'day': 17,
'month': 2,
'time': 365,
'year': 2025},
{'day': 18,
'month': 2,
'time': 83,
'year': 2025},
{'day': 19,
'month': 2,
'time': 137,
'year': 2025},
{'day': 20,
'month': 2,
'time': 249,
'year': 2025},
{'day': 21,
'month': 2,
'time': 479,
'year': 2025},
{'day': 22,
'month': 2,
'time': 10,
'year': 2025},
{'day': 23,
'month': 2,
'time': 93,
'year': 2025},
{'day': 24,
'month': 2,
'time': 137,
'year': 2025},
{'day': 25,
'month': 2,
'time': 67,
'year': 2025}],
'err_code': 0},
'get_monthstat': {'err_code': 0,
'month_list': [{'month': 1,
'time': 3763,
'year': 2025},
{'month': 2,
'time': 4248,
'year': 2025}]},
'get_next_action': {'err_code': 0, 'type': -1},
'get_realtime': {'err_code': -2, 'err_msg': 'member not support'},
'get_rules': {'enable': 1,
'err_code': 0,
'rule_list': [],
'version': 2}},
'smartlife.iot.LAS': {'get_config': {'devs': [{'dark_index': 0,
'enable': 1,
'hw_id': 0,
'level_array': [{'adc': 390,
'name': 'cloudy',
'value': 16},
{'adc': 300,
'name': 'overcast',
'value': 12},
{'adc': 222,
'name': 'dawn',
'value': 9},
{'adc': 222,
'name': 'twilight',
'value': 9},
{'adc': 111,
'name': 'total '
'darkness',
'value': 5},
{'adc': 2400,
'name': 'custom',
'value': 95}],
'max_adc': 2550,
'min_adc': 0}],
'err_code': 0,
'ver': '1.0'},
'get_current_brt': {'err_code': 0, 'value': 5}},
'smartlife.iot.PIR': {'get_adc_value': {'err_code': 0, 'value': 2011},
'get_config': {'array': [80, 50, 20, 0],
'cold_time': 600000,
'enable': 1,
'err_code': 0,
'max_adc': 4095,
'min_adc': 0,
'trigger_index': 1,
'version': '1.0'}},
'smartlife.iot.dimmer': {'get_default_behavior': {'double_click': {'mode': 'unknown'},
'err_code': 0,
'long_press': {'mode': 'unknown'}},
'get_dimmer_parameters': {'bulb_type': 1,
'err_code': 0,
'fadeOffTime': 0,
'fadeOnTime': 0,
'gentleOffTime': 10000,
'gentleOnTime': 3000,
'maxThreshold': 100,
'minThreshold': 14,
'rampRate': 30}},
'system': {'get_sysinfo': {'active_mode': 'none',
'alias': '#MASKED_NAME#',
'brightness': 52,
'dev_name': 'Wi-Fi Smart Dimmer with sensor',
'deviceId': 'REDACTED_3547B7DCA5429DC92708C762180D397',
'err_code': 0,
'feature': 'TIM',
'hold_on': 1,
'hwId': 'REDACTED_F31AF2EA08CD6610E699746',
'hw_ver': '1.0',
'icon_hash': '',
'latitude_i': 0,
'led_off': 0,
'lnk_on': 1,
'longitude_i': 0,
'mac': '40:ED:00:00:00:00',
'max_thr': 1,
'mic_type': 'IOT.SMARTPLUGSWITCH',
'model': 'ES20M(US)',
'next_action': {'type': -1},
'obd_src': 'tplink',
'oemId': 'REDACTED_BABAA04B18C0033D0368A92',
'on_time': 0,
'preferred_state': [{'brightness': 97, 'index': 0},
{'brightness': 75, 'index': 1},
{'brightness': 50, 'index': 2},
{'brightness': 25, 'index': 3}],
'relay_state': 0,
'rssi': -50,
'status': 'new',
'sw_ver': '1.1.5 Build 241206 Rel.142818',
'updating': 0}},
'time': {'get_time': {'err_code': 0,
'hour': 16,
'mday': 25,
'min': 59,
'month': 2,
'sec': 48,
'year': 2025},
'get_timezone': {'err_code': 0, 'index': 18}}}

barney34 avatar Feb 25 '25 21:02 barney34

@barney34, I would suggest to try connect the device to a different network to troubleshoot if this is an issue with your network.

As for the authentication issue that some P110M devices are facing:

  1. There is no special treatment for the KLAP protocol implementation on the Android and iOS app or for how "tss" devices should treat hashing and/or passcode/username and other secrets for handshake1. Which suggests to me that either the mobile client code has not been updated (because deprecated for the P110M devices) or maybe that there is nothing wrong on the client side... which suggests a firmware incompatibility for some P110M devices for the computation of the KLAP hashes or parsing of the credentials and seed.
  2. ~~After inspecting the S/N number of the devices I own, the 224A* and 224B* devices are reported as "tss" and the 224C* device is reported as tp-link. This suggests to me that the 224C* devices may be newer and there could be an incompatibility with the latest firmware on the 224A* and 224B* devices for how KLAP is handled, specifically the hashing operations or parsing the seed/credentials.~~ <-- This has nothing to do with S/N, this is simply because the first device I've plugged in used the tp-link app for onboarding and the following devices used tss! DO NOT USE TSS, only plug in one device at a time to onboard them!
  3. I was not able to obtain a copy of the latest Tapo P110M firmware, and at the moment I am also not able to identify where KLAP handshake1 is implemented on the older firmware (https://github.com/tapo-firmware/Directory/blob/main/all_keys.txt). But I believe that doing a static analysis of the firmware may not reveal any defect about how the hashes are computed... ~~There is a chance it could be a memory management bug on the specific 224A* and 224B* devices for these handshake1 hashing operations~~ or that there is some form of special treatment of the credentials on the tss vs tp-link OBS devices before they are processed. <-- This! Don't use tss!

At this stage this is all speculations... If this is a defect, then they can easily correct it with a firmware update. If this is intentional, until we get a copy of the firmware or they update the same on the iOS/Android apps we will have quite a hard time figuring out which special operations are performed for handshake1...

~~I have ordered a bunch of P110M and will assess if the theory holds - that the 224C* devices and above are obs_src "tp-link" and not affected.~~ I'll update the ticket.

Thireus avatar Feb 26 '25 17:02 Thireus

Alright, I've figured it out! I had a good intuition initially that it had something to do with tss, the solution only became clear once I found what TSS was about...

tss = "TP-Link Simple Setup" - https://community.tp-link.com/en/home/forum/topic/692206

tss is an Oboarding protocol which allows new devices that you plug in to obtain the configuration (credentials and WiFi password) from existing devices on your network.

If you plug in a new device and there are existing Tapo devices on your network that also support tss, then this is the onboarding protocol that will be used, and it would appear that it completely breaks local KLAP. I suppose this might be because the credentials are shared with additional hashing or in a different format when the device uses tss onboarding. So there is indeed a different treatment when tss is used to onboard new devices.

Here is how you can fix the KLAP auth issue on your devices:

  1. Physically remove all existing similar TP-Link devices from your house power sockets.
  2. Plug in only ONE device
  3. Press the power button for 8 seconds (until you see the LED flashing green) - this will factory reset the device
  4. Open the Tapo app on your smartphone and make sure your smartphone bluetooth is on
  5. Find the device model to add, and wait for it to be found (make sure you are close to the device)
  6. Add the device... This will effectively do a "tp-link" onboarding as opposed to a "tss" onboarding
  7. Once the device is added, auth should now work. And --debug should read 'obd_src': 'tp-link' (if not it means you have other TP link devices that support tss plugged in somewhere in your house).
  8. Repeat the procedure for all your devices.

Hope this helps! All my P110M are now working as expected with Home Assistant and they are all on the latest firmware.

Edit: it's still unclear if TP-Link broke KLAP with TSS in the latest firmware because of a new hashing mechanism when TSS has been used at onboarding OR if there is a bug with their firmware... Time will tell I suppose. In the meantime this issue should probably remain opened, because if there is a different hashing mechanism when TSS is used it means it could be ported to python-kasa after reverse engineering it.

Thireus avatar Feb 26 '25 19:02 Thireus

With a few more manual steps inside the Tapo app I was able to add a new P110M device without unplugging all my existing devices:

  1. Press the power button for 8 seconds (until you see the LED flashing green) - this will factory reset the device
  2. Open the Tapo app on your smartphone and make sure your smartphone bluetooth is on
  3. Do not click on anything in the app showing you a newly detected device. Instead, click the + button at the top right, then "Add Device", then choose the model like for example "Plugs" and "Tapo P110M", and then follow the process including selecting your WiFi network, naming the device etc.
  4. After following this procedure, auth now works. And --debug reads 'obd_src': 'tp-link'.

exxamalte avatar May 01 '25 07:05 exxamalte

Just saw this in the Home Assistant Forum and it solved the problem for me:

RyzerPhenix (Stephan Bischof), Feb 8

I just figured it out:
In the Tapo app, go to Me > Tapo Lab > Third-Party Compatibility and enable it. Then, set up a local camera account under Advanced Settings > Camera Account and use these credentials in Home Assistant instead of your TP-Link cloud credentials.

Source: https://community.home-assistant.io/t/tp-link-c110-not-accepting-login/842389

horschdel avatar May 23 '25 13:05 horschdel

I had the same problem with my new Tapo P110M socket plugs and found the solution. When you register your plugs, you must add them manually (one-by-one) from you tapo app on your mobile and not by selecting them with auto found method. I've added my 1st socket plug manually, Add Device -> Plugs -> Tapo P110M, ... , and follow the rest procedure. This worked fine with HA. The next plugs, I've added them by clicking the notifications that new devices found on my mobile app, and then these plugs couldn't add them to HA. 😒 So, I've removed them from my account (tapo mobile app) and then followed my steps with my 1st plug. After, I've added them to HA without any problem. 🙌🚀

kyrtopoulos avatar Jul 01 '25 03:07 kyrtopoulos

This same trick worked with an TAPO S515 switch (which is not listed as working BTW) close the 'found device' screen. add manually. displayed "obd_src": "tss" (20002 port response) now says tplink Thank you for that.

mhench avatar Aug 30 '25 01:08 mhench

I created https://github.com/python-kasa/python-kasa/issues/1573 earlier to make the "unsupported" tss provisioning more visible to help finding workarounds, so I think we can close this issue now.

If someone wants to contribute, a PR to fix this would be very welcome!

rytilahti avatar Sep 21 '25 20:09 rytilahti

I had the same problem with my new Tapo P110M socket plugs and found the solution. When you register your plugs, you must add them manually (one-by-one) from you tapo app on your mobile and not by selecting them with auto found method. I've added my 1st socket plug manually, Add Device -> Plugs -> Tapo P110M, ... , and follow the rest procedure. This worked fine with HA. The next plugs, I've added them by clicking the notifications that new devices found on my mobile app, and then these plugs couldn't add them to HA. 😒 So, I've removed them from my account (tapo mobile app) and then followed my steps with my 1st plug. After, I've added them to HA without any problem. 🙌🚀

It worked for me 🤗

praveshkhatana avatar Nov 03 '25 13:11 praveshkhatana