diff --git a/README.md b/README.md index fd7ec61..73d2ac8 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,7 @@ TinyTuya can also connect to the Tuya Cloud to poll status and issue commands to # Example Usage of TinyTuya import tinytuya -d = tinytuya.OutletDevice('DEVICE_ID_HERE', 'IP_ADDRESS_HERE', 'LOCAL_KEY_HERE') -d.set_version(3.3) +d = tinytuya.Device('DEVICE_ID_HERE', 'IP_ADDRESS_HERE', 'LOCAL_KEY_HERE', version=3.3) data = d.status() print('Device status: %r' % data) ``` @@ -276,8 +275,7 @@ import tinytuya """ OUTLET Device """ -d = tinytuya.OutletDevice('DEVICE_ID_HERE', 'IP_ADDRESS_HERE', 'LOCAL_KEY_HERE') -d.set_version(3.3) +d = tinytuya.Device('DEVICE_ID_HERE', 'IP_ADDRESS_HERE', 'LOCAL_KEY_HERE', version=3.3) data = d.status() # Show status and state of first controlled switch on device @@ -342,13 +340,10 @@ You can set up a persistent connection to a device and then monitor the state ch ```python import tinytuya -d = tinytuya.OutletDevice('DEVICEID', 'DEVICEIP', 'DEVICEKEY') -d.set_version(3.3) -d.set_socketPersistent(True) +d = tinytuya.OutletDevice('DEVICEID', 'DEVICEIP', 'DEVICEKEY', version=3.3, persist=True) print(" > Send Request for Status < ") -payload = d.generate_payload(tinytuya.DP_QUERY) -d.send(payload) +d.status(nowait=True) print(" > Begin Monitor Loop <") while(True): @@ -356,20 +351,20 @@ while(True): data = d.receive() print('Received Payload: %r' % data) - # Send keyalive heartbeat - print(" > Send Heartbeat Ping < ") - payload = d.generate_payload(tinytuya.HEART_BEAT) - d.send(payload) + # Send keep-alive heartbeat + if not data: + print(" > Send Heartbeat Ping < ") + d.heartbeat() # NOTE If you are not seeing updates, you can force them - uncomment: # print(" > Send Request for Status < ") - # payload = d.generate_payload(tinytuya.DP_QUERY) - # d.send(payload) + # d.status(nowait=True) # NOTE Some smart plugs require an UPDATEDPS command to update power data # print(" > Send DPS Update Request < ") # payload = d.generate_payload(tinytuya.UPDATEDPS) # d.send(payload) + ``` ### Tuya Cloud Access @@ -565,9 +560,9 @@ In addition to the built-in `OutletDevice`, `BulbDevice` and `CoverDevice` devic ```python # Example usage of community contributed device modules -from tinytuya import Contrib +from tinytuya.Contrib import ThermostatDevice -thermo = Contrib.ThermostatDevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' ) +thermo = ThermostatDevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' ) ``` ## Tuya Data Points - DPS Table @@ -869,9 +864,9 @@ NOTE (*) - Depending on the firmware, either 18/19/20/26/27 or 108/109/110/111/x A user contributed module is available for this device in the [Contrib library](https://github.com/jasonacox/tinytuya/tree/master/tinytuya/Contrib): ```python -from tinytuya import Contrib +from tinytuya.Contrib import ThermostatDevice -thermo = Contrib.ThermostatDevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' ) +thermo = ThermostatDevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' ) ``` For info on the Sensor Data lists, see https://github.com/jasonacox/tinytuya/discussions/139 diff --git a/RELEASE.md b/RELEASE.md index 4d59017..01ca573 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,9 @@ # RELEASE NOTES +## v1.15.1 - Scanner Fixes + +* Fix scanner broadcast attempting to bind to the wrong IP address, introduced in v1.15.0 + ## v1.15.0 - Scanner Fixes * Fix force-scanning bug in scanner introduced in last release and add broadcast request feature to help discover Tuya version 3.5 devices by @uzlonewolf in https://github.com/jasonacox/tinytuya/pull/511. diff --git a/requirements.txt b/requirements.txt index 3498e2d..c100dc4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ cryptography>=3.1 # Encryption - AES can also be provided via PyCryptodome or pyaes or pyca/cryptography requests # Used for Setup Wizard - Tuya IoT Platform calls colorama # Makes ANSI escape character sequences work under MS Windows. -netifaces # Used to get the IP address of the local machine for scanning for devices. +#netifaces # Used to get the IP address of the local machine for scanning for devices, mainly useful for multi-interface machines. diff --git a/setup.py b/setup.py index 21e06e8..7180267 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ INSTALL_REQUIRES = [ 'requests', # Used for Setup Wizard - Tuya IoT Platform calls 'colorama', # Makes ANSI escape character sequences work under MS Windows. - 'netifaces', # Used for device discovery + #'netifaces', # Used for device discovery, mainly required on multi-interface machines ] CHOOSE_CRYPTO_LIB = [ diff --git a/tinytuya/core.py b/tinytuya/core.py index 0dd98ca..d03dc23 100644 --- a/tinytuya/core.py +++ b/tinytuya/core.py @@ -123,7 +123,7 @@ # Colorama terminal color capability for all platforms init() -version_tuple = (1, 15, 0) +version_tuple = (1, 15, 1) version = __version__ = "%d.%d.%d" % version_tuple __author__ = "jasonacox" diff --git a/tinytuya/scanner.py b/tinytuya/scanner.py index 2f5ed54..8a4fd2a 100644 --- a/tinytuya/scanner.py +++ b/tinytuya/scanner.py @@ -96,13 +96,16 @@ log = logging.getLogger(__name__) # Helper Functions -def getmyIP(): +def getmyIPaddr(): # Fetch my IP address and assume /24 network s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) - r = s.getsockname()[0] + r = str(s.getsockname()[0]) s.close() - r = str(r).split('.') + return r + +def getmyIP(): + r = getmyIPaddr().split('.') # assume a /24 network return '%s.%s.%s.0/24' % tuple(r[:3]) @@ -196,7 +199,7 @@ def get_ip_to_broadcast(): if ip_to_broadcast: return ip_to_broadcast - ip_to_broadcast['255.255.255.255'] = getmyIP() + ip_to_broadcast['255.255.255.255'] = getmyIPaddr() return ip_to_broadcast def send_discovery_request( iface_list=None ): @@ -215,7 +218,11 @@ def send_discovery_request( iface_list=None ): if 'socket' not in iface: iface['socket'] = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP iface['socket'].setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - iface['socket'].bind( (address,0) ) + try: + iface['socket'].bind( (address,0) ) + except: + log.debug( 'Failed to bind to address %r for discovery broadcasts, skipping interface!', address, exc_info=True ) + continue if 'payload' not in iface: bcast = json.dumps( {"from":"app","ip":address} ).encode()