Skip to content

Commit

Permalink
Merge branch 'argparse' of https://github.com/uzlonewolf/tinytuya int…
Browse files Browse the repository at this point in the history
…o pr/503
  • Loading branch information
jasonacox committed May 26, 2024
2 parents 9a17a0a + 9ec1d25 commit b14c23e
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 116 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,6 @@ mappings.json
# backup files
*.bak
*~

# test scripts
server/r
209 changes: 108 additions & 101 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ python -m tinytuya scan

TinyTuya has a built-in setup Wizard that uses the Tuya IoT Cloud Platform to generate a JSON list (devices.json) of all your registered devices, including secret *Local_Key* and *Name* of your devices. Follow the steps below:

1. PAIR - Download the *Smart Life App* or *Tuya Smart App*, available for [iPhone](https://itunes.apple.com/us/app/smart-life-smart-living/id1115101477?mt=8) or [Android](https://play.google.com/store/apps/details?id=com.tuya.smartlife&hl=en). Set up your SmartLife account and pair all of your Tuya devices (this is important as you cannot access a device that has not been paired).
1. PAIR - Download the *Smart Life App* or *Tuya Smart App*, available for [iPhone](https://itunes.apple.com/us/app/smart-life-smart-living/id1115101477?mt=8) or [Android](https://play.google.com/store/apps/details?id=com.tuya.smartlife&hl=en). Set up your SmartLife account and pair all of your Tuya devices (this is important as you cannot access a device that has not been paired). Do not use a 'guest' account, otherwise it will get deleted without confirmation at step 3.

2. SCAN (Optional) - Run the TinyTuya scan to get a list of Tuya devices on your network. It will show device *Address*, *Device ID* and *Version* number (3.x):
```bash
Expand Down Expand Up @@ -130,107 +130,114 @@ d.turn_off()

### TinyTuya Module Classes and Functions
```
Global Functions
devices = deviceScan() # Returns dictionary of devices found on local network
scan() # Interactive scan of local network
wizard() # Interactive setup wizard
set_debug(toggle, color) # Activate verbose debugging output
Classes
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
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
Cloud(apiRegion, apiKey, apiSecret, apiDeviceID, new_sign_algorithm)
Functions:
Configuration Settings:
set_version(version) # Set device version 3.1 [default] or 3.3 (all new devices)
set_socketPersistent(False/True) # Keep connect open with device: False [default] or True
set_socketNODELAY(False/True) # Add cooldown period for slow Tuya devices: False or True [default]
set_socketRetryLimit(integer) # Set retry count limit [default 5]
set_socketTimeout(s) # Set connection timeout in seconds [default 5]
set_dpsUsed(dpsUsed) # Set data points (DPs) to expect (rarely needed)
set_retry(retry=True) # Force retry if response payload is truncated
set_sendWait(num_secs) # Seconds to wait after sending for a response
set_bulb_type(type): # For BulbDevice, set type to A, B or C
Device Commands:
status() # Fetch status of device (json payload)
subdev_query(nowait) # query sub-device list and online status (only for gateway devices)
detect_available_dps() # Return list of DPS available from device
set_status(on, switch=1, nowait) # Control status of the device to 'on' or 'off' (bool)
# nowait (default False) True to send without waiting for response
set_value(index, value, nowait) # Send and set value of any DPS/index on device.
set_multiple_values(index_value_dict, nowait)
# Set multiple values with a single request
# Note: Some devices do not like this!
heartbeat(nowait) # Send heartbeat to device
updatedps(index=[1], nowait) # Send updatedps command to device to refresh DPS values
turn_on(switch=1, nowait) # Turn on device / switch #
turn_off(switch=1, nowait) # Turn off device
set_timer(num_secs, nowait) # Set timer for num_secs on devices (if supported)
generate_payload(command, data) # Generate TuyaMessage payload for command with data
send(payload) # Send payload to device (do not wait for response)
receive() # Receive payload from device
OutletDevice:
set_dimmer(percentage):
CoverDevice:
open_cover(switch=1):
close_cover(switch=1):
stop_cover(switch=1):
BulbDevice
set_colour(r, g, b, nowait):
set_hsv(h, s, v, nowait):
set_white(brightness, colourtemp, nowait):
set_white_percentage(brightness=100, colourtemp=0, nowait):
set_brightness(brightness, nowait):
set_brightness_percentage(brightness=100, nowait):
set_colourtemp(colourtemp, nowait):
set_colourtemp_percentage(colourtemp=100, nowait):
set_scene(scene, nowait): # 1=nature, 3=rave, 4=rainbow
set_mode(mode='white', nowait): # white, colour, scene, music
result = brightness():
result = colourtemp():
(r, g, b) = colour_rgb():
(h,s,v) = colour_hsv():
result = state():
Cloud
setregion(apiRegion)
cloudrequest(url, action=[POST if post else GET], post={}, query={})
getdevices(verbose=False)
getstatus(deviceid)
getfunctions(deviceid)
getproperties(deviceid)
getdps(deviceid)
sendcommand(deviceid, commands [, uri])
getconnectstatus(deviceid)
getdevicelog(deviceid, start=[now - 1 day], end=[now], evtype="1,2,3,4,5,6,7,8,9,10", size=0, max_fetches=50, start_row_key=None, params={})
AESCipher - Cryptography Helpers
XenonDevice(args...) - Base Class
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
Cloud(apiRegion, apiKey, apiSecret, apiDeviceID, new_sign_algorithm)
TinyTuya Base Functions
devices = deviceScan() # Returns dictionary of devices found on local network
scan() # Interactive scan of local network
wizard() # Interactive setup wizard
set_debug(toggle, color) # Activate verbose debugging output
pack_message(msg, hmac_key) # Packs a TuyaMessage(), encrypting or adding a CRC if required
unpack_message(data, hmac_key, header,
no_retcode) # Unpacks a TuyaMessage()
parse_header(data) # Unpacks just the header part of a message into a TuyaHeader()
find_device(dev_id, address) # Scans network for Tuya devices with either ID = dev_id or IP = address
device_info(dev_id) # Searches DEVICEFILE (usually devices.json) for device with ID
assign_dp_mappings(tuyadevices, mappings) # Adds mappings to all the devices in the tuyadevices list
decrypt_udp(msg) # Decrypts a UDP network broadcast packet
Device Functions (All Devices)
json = status() # returns json payload
subdev_query(nowait) # query sub-device status (only for gateway devices)
set_version(version) # 3.1 [default], 3.2, 3.3 or 3.4
set_socketPersistent(False/True) # False [default] or True
set_socketNODELAY(False/True) # False or True [default]
set_socketRetryLimit(integer) # retry count limit [default 5]
set_socketRetryDelay(integer) # retry delay [default 5]
set_socketTimeout(timeout) # set connection timeout in seconds [default 5]
set_dpsUsed(dps_to_request) # add data points (DPS) to request
add_dps_to_request(index) # add data point (DPS) index set to None
set_retry(retry=True) # retry if response payload is truncated
set_status(on, switch=1, nowait) # Set status of switch to 'on' or 'off' (bool)
set_value(index, value, nowait) # Set int value of any index.
set_multiple_values(index_value_dict, nowait) # Set multiple values with a single request
heartbeat(nowait) # Send heartbeat to device
updatedps(index=[1], nowait) # Send updatedps command to device
turn_on(switch=1, nowait) # Turn on device / switch #
turn_off(switch=1, nowait) # Turn off
set_timer(num_secs, nowait) # Set timer for num_secs
set_sendWait(num_secs) # Time to wait after sending commands before pulling response
detect_available_dps() # Return list of DPS available from device
generate_payload(command, data,... # Generate TuyaMessage payload for command with data
send(payload) # Send payload to device (do not wait for response)
receive() # Receive payload from device
OutletDevice Additional Functions
set_dimmer(percentage):
BulbDevice Additional Functions
set_colour(r, g, b, nowait):
set_hsv(h, s, v, nowait):
set_white(brightness, colourtemp, nowait):
set_white_percentage(brightness=100, colourtemp=0, nowait):
set_brightness(brightness, nowait):
set_brightness_percentage(brightness=100, nowait):
set_colourtemp(colourtemp, nowait):
set_colourtemp_percentage(colourtemp=100, nowait):
set_scene(scene, nowait): # 1=nature, 3=rave, 4=rainbow
set_mode(mode='white', nowait): # white, colour, scene, music
result = brightness():
result = colourtemp():
(r, g, b) = colour_rgb():
(h,s,v) = colour_hsv()
result = state():
CoverDevice Additional Functions
open_cover(switch=1):
close_cover(switch=1):
stop_cover(switch=1):
Cloud Functions
setregion(apiRegion)
cloudrequest(url, action=[POST if post else GET], post={}, query={})
getdevices(verbose=False)
getstatus(deviceid)
getfunctions(deviceid)
getproperties(deviceid)
getdps(deviceid)
sendcommand(deviceid, commands [, uri])
getconnectstatus(deviceid)
getdevicelog(deviceid, start=[now - 1 day], end=[now], evtype="1,2,3,4,5,6,7,8,9,10", size=100, params={})
-> when start or end are negative, they are the number of days before "right now"
i.e. "start=-1" is 1 day ago, "start=-7" is 7 days ago
```

### TinyTuya Error Codes
Expand Down
7 changes: 5 additions & 2 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
FROM python:3.10
FROM python:3.12-bookworm
WORKDIR /app
ENV PYTHONUNBUFFERED 1
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
RUN pip3 install cryptography pycryptodome --no-build-isolation
RUN pip3 install --no-cache-dir tinytuya
COPY . .
CMD ["python3", "server.py"]
EXPOSE 8888
EXPOSE 8888
EXPOSE 6666/udp
EXPOSE 6667/udp
25 changes: 15 additions & 10 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ Starting threads...

```bash
docker run \
-d \
-p 8888:8888 \
-p 6666:6666/udp \
-p 6667:6667/udp \
-e DEBUG='no' \
--mount type=bind,source="$(pwd)"/devices.json,target=/app/devices.json \
--mount type=bind,source="$(pwd)"/tinytuya.json,target=/app/tinytuya.json \
--name tinytuya \
--restart unless-stopped \
jasonacox/tinytuya
-d \
-p 8888:8888 \
-p 6666:6666/udp \
-p 6667:6667/udp \
-e DEBUG='no' \
-v $PWD/devices.json:/app/devices.json \
-v $PWD/tinytuya.json:/app/tinytuya.json \
--name tinytuya \
--restart unless-stopped \
jasonacox/tinytuya
```

2. Test the Server
Expand Down Expand Up @@ -110,6 +110,11 @@ The UI at http://localhost:8888 allows you to view and control the devices.

## Release Notes

### t11 - Minimize Container

* Reduce size of Docker container by removing rust build and using python:3.12-bookworm.
* Add signal handler for cleaner shutdown handling for `docker stop`.

### t10 - Remove Import

* Remove unused imports for Crypto.
Expand Down
9 changes: 8 additions & 1 deletion server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
print("WARN: Check dependencies. See https://github.com/jasonacox/tinytuya/issues/377")
print("WARN: Error: {}.".format(impErr.args[0]))
import resource
import signal
import sys
import os
import urllib.parse
Expand All @@ -59,7 +60,7 @@

import tinytuya

BUILD = "t10"
BUILD = "t11"

# Defaults
APIPORT = 8888
Expand Down Expand Up @@ -96,6 +97,12 @@
log.debug("TinyTuya Server [%s]", BUILD)
tinytuya.set_debug(True)

# Signal handler - Exit on SIGTERM
def sig_term_handle(signum, frame):
raise SystemExit

signal.signal(signal.SIGTERM, sig_term_handle)

# Static Assets
web_root = os.path.join(os.path.dirname(__file__), "web")

Expand Down
20 changes: 18 additions & 2 deletions tinytuya/Contrib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,30 @@ In addition to the built-in `OutletDevice`, `BulbDevice` and `CoverDevice` devic
* Example: [examples/IRRemoteControlDevice-example.py](https://github.com/jasonacox/tinytuya/blob/master/examples/Contrib/IRRemoteControlDevice-example.py)

```python
# Example usage of community contributed device modules
# Example 1 -usage of community contributed device modules
from tinytuya import Contrib

ir = Contrib.IRRemoteControlDevice( 'abcdefghijklmnop123456', '172.28.321.475', '1234567890123abc' )
ir = Contrib.IRRemoteControlDevice( 'abcdefghijklmnop123456', '10.2.3.4', '1234567890123abc' )
button = ir.receive_button(timeout=15)
ir.send_button(button)
```

```python
# Example 2 - Aubess WiFi IR Controller S16 for Sony TV - Issue #492
from tinytuya import Contrib

# Pull the Device Log from Tuya cloud while using Tuya Smart App and pressing PWR button on controller:
# SONY Tuya Device Debug Log: IR send{"control":"send_ir","head":"xxxx","key1":"003xxx)","type":0,"delay":300}
head = 'xxx'
key1 = '003xxx'

ir = Contrib.IRRemoteControlDevice( 'abcdefghijklmnop123456', '10.2.3.4', '1234567890123abc', persist=True )
ir.send_key( head, key1 )

# NOTE: If it doesn't work, try removing a leading zero from key1. Depending on what DPS set the device
# uses the key1 from the debug logs could have an extra 0.
```

### SocketDevice

* SocketDevice - A community-contributed Python module to add support for Tuya WiFi smart sockets
Expand Down

0 comments on commit b14c23e

Please sign in to comment.