jasonacox / tinytuya

Python API for Tuya WiFi smart devices using a direct local area network (LAN) connection or the cloud (TuyaCloud API).
MIT License
867 stars 157 forks source link

No Timeout or Error, Just Hangs #501

Closed jasonacox closed 1 month ago

jasonacox commented 1 month ago

Problem reported:

I’m using the library but when we run the status call it just holds there when no device is online, it doesn’t fail or error it just hangs there waiting for it? Is there a way for it to throw an error or just return “false” or something if a Tuya device isn’t present?

Peggun commented 1 month ago

Hey there! I am working on the same project as the person who submitted this issue. Here is the problem. Like it says, it just hangs. We have a python script that runs led screens like a scoreboard. When the scripts get to checking the status of the Siren Plug, the script just hangs, and waits for the siren to be found indefinitely. Here is an example.

# Connect to device
        sirenGPO = tinytuya.OutletDevice(
             dev_id='106030143c6105f6f7d7',
             address=sirenGPOIP,
             local_key='9b0609b0cfe49d5c', 
             version=3.3)

        apiResponse = requests.get(url="http://localhost/getSirenAvailability.php")

        sirenPresentJson = apiResponse.json()

        if sirenPresentJson['SirenPresent'] == "1":
            sirenGPO.status()
            sirenGPO.turn_off()
            sirenPresent = True

We had to bypass this issue by adding a coloum in our SQL Database and use the getSirenAvailability.php file to see if the siren is there based on the user. If you run our entire script and remove all of the SQL calls, the script just hangs until it can find the siren. This solution by bypassing it took some time to figure out, as I feel like that there should be a timer on the status() call, and so that if it returns false or an error, we can just skip that can put it into a variable for example:

sirenPresent = sirenGPO.status()

This will help as then we can set the variable as the return variable and check to see if there is a status after the timeout limit has been reached. Hopefully, you can fix this issue relatively soon, but thanks anyways. Kind Regards.

Peggun commented 1 month ago

Hi there again. I had just tested the timeout and it in fact does timeout, however it takes 45 seconds. I feel like this is too long in some circumstances, and that you either make like 10 seconds, or add a argument that the user could change the time for the timeout to occur in. here is the script that i used to time. I measured the time on my phone and it took exactly 45 seconds.

import tinytuya

def main():
    sirenGPO = tinytuya.OutletDevice(
        dev_id='106030143c6105f6f7d7',
        address='{ipaddress}',
        local_key='9b0609b0cfe49d5c', 
        version=3.3)

    gpoStatus = sirenGPO.status()
    print(gpoStatus)

if __name__ == '__main__':
    main()

It does print an error, but I feel like that is way too long for 45 seconds. Anyways, thanks for your help with this issue. We just believe that for a scoreboard, 45 seconds is just too long for our needs. I didnt want to put the IP just because i'm not very sure

uzlonewolf commented 1 month ago

or add a argument that the user could change the time for the timeout to occur in

This already exists. There are 3 variables that control how long it will take to timeout: connection_timeout (default: 5 seconds), connection_retry_limit (default: 5), and connection_retry_delay (default: 5 seconds). Add one or more of those to the tinytuya.OutletDevice( ... ) line with lower values to reduce the amount of time it will spend trying to connect.

Total timeout = (connection_timeout connection_retry_limit) + (connection_retry_delay (connection_retry_limit - 1))

Peggun commented 1 month ago

or add a argument that the user could change the time for the timeout to occur in

This already exists. There are 3 variables that control how long it will take to timeout: connection_timeout (default: 5 seconds), connection_retry_limit (default: 5), and connection_retry_delay (default: 5 seconds). Add one or more of those to the tinytuya.OutletDevice( ... ) line with lower values to reduce the amount of time it will spend trying to connect.

Total timeout = (connection_timeout connection_retry_limit) + (connection_retry_delay (connection_retry_limit - 1))

Thanks for the help! I didn't know that beforehand, now when i test it works great. Thanks for the assistance :)

jasonacox commented 1 month ago

Thanks @uzlonewolf and thanks @GamerPeggun for opening this. We should add some documentation on this to make it easier to find. I'll flag that.

Also...

I feel like that is way too long for 45 seconds.

Understandable. I wonder, is the current "default" is this too long? Because the Tuya devices can be difficult to reach at times, the retry is often necessary. And the longer timeouts and delays are also often necessary to get a connection. But perhaps they are too high or 5 retries is too much?

I think we can leave this Issue open and gather feedback from the community if there is interest in lowering the default.

Example showing defaults:

import tinytuya

d = tinytuya.OutletDevice(
    dev_id='{deviceid}',
    address='{ipaddress}',
    local_key='{key}',
    connection_timeout=5,
    connection_retry_limit=5,
    connection_retry_delay=5,
    version=3.3)

print(d.status())
Peggun commented 1 month ago

Now that i have noticed that you can change the time, for our circumstance, with a high pace game that is stop and start, and when you press a button it waits 45 seconds after the game has started to find it, it was too long. However, with the TinyTuya devices being too long to find, and that we can change it, it is understandable for the search of the devices to be something like 45 seconds.

Thanks for all of the help on this @jasonacox and @uzlonewolf ! :)

jasonacox commented 1 month ago

Updated documentation:

    Device(args...) - Tuya Class for Devices
      OutletDevice(args...)
      CoverDevice(args...)
      BulbDevice(args...)
        Where args:
          dev_id (str): Device ID e.g. 01234567891234567890
          address (str): Device Network IP Address e.g. 10.0.1.99 or "Auto" to auto-find
          local_key (str): The encryption key
          dev_type (str): Device type for payload options (see below)
          version = 3.1 (float): Tuya Protocol (e.g. 3.1, 3.2, 3.3, 3.4, 3.5)
          persist = False (bool): Keep TCP link open
          cid = None (str): Optional sub device id
          node_id = None (str): Alias for cid
          parent = None (object): Gateway device object this is a child of
          port = TCPPORT (int): The port to connect to device
          connection_timeout = 5 (int): Timeout in seconds
          connection_retry_limit = 5 (int)
          connection_retry_delay = 5 (int)

          Total timeout = (connection_timeout * connection_retry_limit) + 
                          (connection_retry_delay * (connection_retry_limit - 1))
                          Defaults: (5 * 5) + (5 * (5 - 1)) = 45 seconds