From b327a671538d7d74543b8426701c30baa979f9a3 Mon Sep 17 00:00:00 2001 From: mschlenstedt Date: Sun, 21 May 2023 14:39:43 +0200 Subject: [PATCH 1/3] Use {DeviceName} instead of {DeviceID} alternatively for API commands --- server/README.md | 27 +++++++++++++++------------ server/server.py | 43 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/server/README.md b/server/README.md index 01df7242..6fbd390b 100644 --- a/server/README.md +++ b/server/README.md @@ -9,21 +9,24 @@ The TinyTuya API Server provides a central service to access all your Tuya devic API Functions - The server listens for GET requests on local port 8888: ``` - /help - List all available commands - /devices - List all devices discovered with metadata - /device/{DeviceID} - List specific device metadata - /numdevices - List current number of devices discovered - /status/{DeviceID} - List current device status - /set/{DeviceID}/{Key}/{Value} - Set DPS {Key} with {Value} - /turnon/{DeviceID}/{SwitchNo} - Turn on device, optional {SwtichNo} - /turnoff/{DeviceID}/{SwitchNo} - Turn off device, optional {SwtichNo} - /delayedoff/{DeviceID}/{SwitchNo}/{Seconds} - Turn off device with a delay, optional {SwitchNo}/{Delay} - /sync - Fetches the device list and local keys from the Tuya Cloud API + /help - List all available commands + /devices - List all devices discovered with metadata + /device/{DeviceID}|{DeviceName} - List specific device metadata + /numdevices - List current number of devices discovered + /status/{DeviceID}|{DeviceName} - List current device status + /set/{DeviceID}|{DeviceName}/{Key}/{Value} - Set DPS {Key} with {Value} + /turnon/{DeviceID}/{SwitchNo} - Turn on device, optional {SwtichNo} + /turnoff/{DeviceID}/{SwitchNo} - Turn off device, optional {SwtichNo} + /delayedoff/{DeviceID}|{DeviceName}/{SwitchNo}/{Seconds} + - Turn off device with a delay, optional {SwitchNo}/{Delay} + /sync - Fetches the device list and local keys from the Tuya Cloud API /cloudconfig/{apiKey}/{apiSecret}/{apiRegion}/{apiDeviceID} - - Sets the Tuya Cloud API login info - /offline - List of registered devices that are offline + - Sets the Tuya Cloud API login info + /offline - List of registered devices that are offline ``` +Note! If oyu use {DeviceName} instead of {DeviceID}, make sure your Device Names are absolutely unique! Otherwise you will get funny results. + ## Quick Start This folder contains the `server.py` script that runs a simple python based webserver that makes the TinyTuya API calls. Make sure the `device.json` file is the same directory where you start the server. diff --git a/server/server.py b/server/server.py index 1e9b34a8..3d87591f 100644 --- a/server/server.py +++ b/server/server.py @@ -41,6 +41,7 @@ import urllib.parse from http.server import BaseHTTPRequestHandler, HTTPServer, ThreadingHTTPServer from socketserver import ThreadingMixIn +import urllib.parse # Required module: pycryptodome try: @@ -349,13 +350,13 @@ def do_GET(self): elif self.path == '/help': # show available commands cmds = [("/devices","List all devices discovered with metadata"), - ("/device/{DeviceID}", "List specific device metadata"), + ("/device/{DeviceID}|{DeviceName}", "List specific device metadata"), ("/numdevices", "List current number of devices discovered"), - ("/status/{DeviceID}", "List current device status"), - ("/set/{DeviceID}/{Key}/{Value}", "Set DPS {Key} with {Value}"), - ("/turnon/{DeviceID}/{SwitchNo}", "Turn on device, optional {SwtichNo}"), - ("/turnoff/{DeviceID}/{SwitchNo}", "Turn off device, optional {SwtichNo}"), - ("/delayoff/{DeviceID}/{SwitchNo}/{Time}", "Turn off device with delay of 10 secs, optional {SwitchNo}/{Time}"), + ("/status/{DeviceID}|{DeviceName}", "List current device status"), + ("/set/{DeviceID}|{DeviceName}/{Key}/{Value}", "Set DPS {Key} with {Value}"), + ("/turnon/{DeviceID}|{DeviceName}/{SwitchNo}", "Turn on device, optional {SwtichNo}"), + ("/turnoff/{DeviceID}|{DeviceName}/{SwitchNo}", "Turn off device, optional {SwtichNo}"), + ("/delayoff/{DeviceID}|{DeviceName}/{SwitchNo}/{Time}", "Turn off device with delay of 10 secs, optional {SwitchNo}/{Time}"), ("/sync", "Fetches the device list and local keys from the Tuya Cloud API"), ("/cloudconfig/{apiKey}/{apiSecret}/{apiRegion}/{apiDeviceID}", "Sets the Tuya Cloud API login info"), ("/offline", "List of registered devices that are offline")] @@ -384,6 +385,11 @@ def do_GET(self): except: message = json.dumps({"Error": "Syntax error in set command URL.", "url": self.path}) log.debug("Syntax error in set command URL: %s" % self.path) + if(id not in deviceslist): + for key in deviceslist: + if deviceslist[key]['name'] == urllib.parse.unquote(id): + id = deviceslist[key]['id'] + break if(id in deviceslist): d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) d.set_version(float(deviceslist[id]["version"])) @@ -394,6 +400,11 @@ def do_GET(self): log.debug("Device ID not found: %s" % id) elif self.path.startswith('/device/'): id = self.path.split('/device/')[1] + if(id not in deviceslist): + for key in deviceslist: + if deviceslist[key]['name'] == urllib.parse.unquote(id): + id = deviceslist[key]['id'] + break if(id in deviceslist): message = json.dumps(deviceslist[id]) else: @@ -418,6 +429,11 @@ def do_GET(self): id = "" message = json.dumps({"Error": "Invalid syntax in turnoff command.", "url": self.path}) log.debug("Syntax error in in turnoff command: %s" % self.path) + if(id not in deviceslist): + for key in deviceslist: + if deviceslist[key]['name'] == urllib.parse.unquote(id): + id = deviceslist[key]['id'] + break if id in deviceslist: try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) @@ -441,6 +457,11 @@ def do_GET(self): id = "" message = json.dumps({"Error": "Invalid syntax in delayoff command.", "url": self.path}) log.debug("Syntax error in in delayoff command: %s" % self.path) + if(id not in deviceslist): + for key in deviceslist: + if deviceslist[key]['name'] == urllib.parse.unquote(id): + id = deviceslist[key]['id'] + break if id in deviceslist: try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) @@ -466,6 +487,11 @@ def do_GET(self): id = "" message = json.dumps({"Error": "Invalid syntax in turnon command.", "url": self.path}) log.debug("Syntax error in turnon command URL: %s" % self.path) + if(id not in deviceslist): + for key in deviceslist: + if deviceslist[key]['name'] == urllib.parse.unquote(id): + id = deviceslist[key]['id'] + break if id in deviceslist: try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) @@ -485,6 +511,11 @@ def do_GET(self): message = json.dumps(jout) elif self.path.startswith('/status/'): id = self.path.split('/status/')[1] + if(id not in deviceslist): + for key in deviceslist: + if deviceslist[key]['name'] == urllib.parse.unquote(id): + id = deviceslist[key]['id'] + break if(id in deviceslist): try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) From 6be7eef04a69d78bacdc1f6cc6ac2a583950a0fb Mon Sep 17 00:00:00 2001 From: mschlenstedt Date: Mon, 22 May 2023 21:33:01 +0200 Subject: [PATCH 2/3] getDeviceIDbyName as seperate function --- server/server.py | 62 ++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/server/server.py b/server/server.py index 3d87591f..f88d4d7b 100644 --- a/server/server.py +++ b/server/server.py @@ -9,21 +9,24 @@ Description Server continually listens for Tuya UDP discovery packets and updates the database of devices - and uses devices.json to determine metadata about devices. + and uses devices.json to determine metadata about devices. Server listens for GET requests on local port 8888: - /devices - List all devices discovered with metadata - /device/{DeviceID} - List specific device metadata + /devices - List all devices discovered with metadata + /device/{DeviceID}|{DeviceName} - List specific device metadata /numdevices - List current number of devices discovered - /status/{DeviceID} - List current device status - /set/{DeviceID}/{Key}/{Value} - Set DPS {Key} with {Value} - /turnon/{DeviceID}/{SwitchNo} - Turn on device, optional {SwtichNo} - /turnoff/{DeviceID}/{SwitchNo} - Turn off device, optional {SwtichNo} + /status/{DeviceID}|{DeviceName} - List current device status + /set/{DeviceID}|{DeviceName}/{Key}/{Value} + - Set DPS {Key} with {Value} + /turnon/{DeviceID}|{DeviceName}/{SwitchNo} + - Turn on device, optional {SwtichNo} + /turnoff/{DeviceID}|{DeviceName}/{SwitchNo} + - Turn off device, optional {SwtichNo} /sync - Fetches the device list and local keys from the Tuya Cloud API - /cloudconfig/{apiKey}/{apiSecret}/{apiRegion}/{apiDeviceID} + /cloudconfig/{apiKey}/{apiSecret}/{apiRegion}/{apiDeviceID} - Sets the Tuya Cloud API login info /offline - List of registered devices that are offline - - /delayoff/{DeviceID}/{Time} - Turn off device with delay + /delayoff/{DeviceID}|{DeviceName}/{Time} + - Turn off device with delay """ @@ -247,6 +250,15 @@ def tuyaCloudRefresh(): tuyaSaveJson() return {'devices': tuyadevices} +def getDeviceIdByName(name): + id = False + nameuq = urllib.parse.unquote(name) + for key in deviceslist: + if deviceslist[key]['name'] == nameuq: + id = deviceslist[key]['id'] + break + return (id) + # Threads def tuyalisten(port): """ @@ -386,10 +398,7 @@ def do_GET(self): message = json.dumps({"Error": "Syntax error in set command URL.", "url": self.path}) log.debug("Syntax error in set command URL: %s" % self.path) if(id not in deviceslist): - for key in deviceslist: - if deviceslist[key]['name'] == urllib.parse.unquote(id): - id = deviceslist[key]['id'] - break + id = getDeviceIdByName(id) if(id in deviceslist): d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) d.set_version(float(deviceslist[id]["version"])) @@ -401,10 +410,7 @@ def do_GET(self): elif self.path.startswith('/device/'): id = self.path.split('/device/')[1] if(id not in deviceslist): - for key in deviceslist: - if deviceslist[key]['name'] == urllib.parse.unquote(id): - id = deviceslist[key]['id'] - break + id = getDeviceIdByName(id) if(id in deviceslist): message = json.dumps(deviceslist[id]) else: @@ -430,10 +436,7 @@ def do_GET(self): message = json.dumps({"Error": "Invalid syntax in turnoff command.", "url": self.path}) log.debug("Syntax error in in turnoff command: %s" % self.path) if(id not in deviceslist): - for key in deviceslist: - if deviceslist[key]['name'] == urllib.parse.unquote(id): - id = deviceslist[key]['id'] - break + id = getDeviceIdByName(id) if id in deviceslist: try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) @@ -458,10 +461,7 @@ def do_GET(self): message = json.dumps({"Error": "Invalid syntax in delayoff command.", "url": self.path}) log.debug("Syntax error in in delayoff command: %s" % self.path) if(id not in deviceslist): - for key in deviceslist: - if deviceslist[key]['name'] == urllib.parse.unquote(id): - id = deviceslist[key]['id'] - break + id = getDeviceIdByName(id) if id in deviceslist: try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) @@ -488,10 +488,7 @@ def do_GET(self): message = json.dumps({"Error": "Invalid syntax in turnon command.", "url": self.path}) log.debug("Syntax error in turnon command URL: %s" % self.path) if(id not in deviceslist): - for key in deviceslist: - if deviceslist[key]['name'] == urllib.parse.unquote(id): - id = deviceslist[key]['id'] - break + id = getDeviceIdByName(id) if id in deviceslist: try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) @@ -512,10 +509,7 @@ def do_GET(self): elif self.path.startswith('/status/'): id = self.path.split('/status/')[1] if(id not in deviceslist): - for key in deviceslist: - if deviceslist[key]['name'] == urllib.parse.unquote(id): - id = deviceslist[key]['id'] - break + id = getDeviceIdByName(id) if(id in deviceslist): try: d = tinytuya.OutletDevice(id, deviceslist[id]["ip"], deviceslist[id]["key"]) From d53478843efc2fb0adbb4776d769f3c384e04eb4 Mon Sep 17 00:00:00 2001 From: mschlenstedt Date: Mon, 22 May 2023 21:35:04 +0200 Subject: [PATCH 3/3] Typo --- server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index 6fbd390b..33017643 100644 --- a/server/README.md +++ b/server/README.md @@ -25,7 +25,7 @@ API Functions - The server listens for GET requests on local port 8888: /offline - List of registered devices that are offline ``` -Note! If oyu use {DeviceName} instead of {DeviceID}, make sure your Device Names are absolutely unique! Otherwise you will get funny results. +Note! If you use {DeviceName} instead of {DeviceID}, make sure your Device Names are absolutely unique! Otherwise you will get funny results. ## Quick Start