mihai-dinculescu / tapo

Unofficial Tapo API Client. Works with TP-Link Tapo smart devices. Tested with light bulbs (L510, L520, L530, L610, L630), light strips (L900, L920, L930), plugs (P100, P105, P110, P115, P300), hubs (H100), switches (S200B) and sensors (KE100, T100, T110, T300, T310, T315).
MIT License
313 stars 30 forks source link

S500D support #216

Open smatterchoo opened 1 month ago

smatterchoo commented 1 month ago

Thanks for building this and making it easy to use/install via pip!

I am able to turn my Tapo S500D smart dimmer switch on and off using the tapo library's generic device, but I'm unable to run this sample code, getting an error in the get_device_info() line.

client = ApiClient(USER,PW)
device = await client.generic_device(TAPO_IPADDR)
device_info = await device.get_device_info()
if device_info.device_on == True:
    print("Device is on. Turning it off...")
    await device.off()
elif device_info.device_on == False:
    print("Device is off. Turning it on...")
    await device.on()
else:
    print("This device does not support on/off functionality.")

The error is

File "/home/alarm_user/alarm2024/tapotest.py", line 14, in main
device_info = await device.get_device_info()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Exception: Serde(Error("missing field `overheated`", line: 1, column: 813))  

Python 3.11.2, tapo 0.3.0 installed via pip 24.0 on a raspberry pi running Raspbian GNU/Linux 12 (bookworm)

smatterchoo commented 1 month ago

Actually although get_device_info() fails, get_device_info_json() does work. it looks like the "overheated" field is now named "overheat_status" for this device. Here is the json with all keys and most values (some redacted for privacy).

{'avatar': 'switch_s500d', 'brightness': 50, 
'default_states': {'re_power_type': 'always_off', 're_power_type_capability': ['last_states', 'always_on', 'always_off'], 'type': 'last_states'}, 
'device_id': 'xxxx', 'device_on': False, 'fw_id': '00000000000000000000000000000000', 
'fw_ver': '1.1.1 Build 240130 Rel.090201', 'has_set_location_info': True, 
'hw_id': 'xxxx', 'hw_ver': '1.0', 'ip': 'xxxx', 'lang': 'en_US', 
'latitude': xxx, 'longitude': -xxx, 'mac': 'xxxx', 'model': 'S500D', 
'nickname': 'xxxxx=', 'oem_id': 'xxxxx', 'on_time': 0, 
'overheat_status': 'normal', 'region': 'America/Los_Angeles', 
'rssi': -43, 'signal_level': 3, 'specs': '', 'ssid': 'xxxxx', 'time_diff': -48\0, 
'type': 'SMART.TAPOSWITCH'}  

In case it's helpful to other users, you can roll your own device info from the json like this:

    device_info_dict = await device.get_device_info_json()
    DeviceInfoRecord = namedtuple('DeviceInfoRecord', device_info_dict.keys())
    device_info = DeviceInfoRecord(**device_info_dict)

    print(f"Device is {'on' if device_info.device_on else 'off'}")
    if device_info.device_on: 

..etc

mihai-dinculescu commented 1 month ago

Thank you for raising this issue.

In the short term, I have removed the overheated property from DeviceInfoGenericResult.

In the long term, I think it's worth adding first-class support for S500D devices. It looks very similar to light bulb devices, which can be turned on/off and have their brightness adjusted.

Thank you for posting the device info, that's very helpful!

smatterchoo commented 1 month ago

As one user of your library, I would be very happy if you do not first-class the S500D but instead make the generic tapo device more functional. get_device_info_json() is perfect but if you could add set_device_info_json(<key/val dict>), it would support all devices (that use the same authentication method) and there's no new work for you every time Tapo introduces a new device or change.