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

TreatLife Product #34

Closed sjpbailey closed 1 year ago

sjpbailey commented 3 years ago

Hello Jason! I am working on TreatLife switches and LED lights and have some details, along with some code I have been fiddling with for a couple of weeks to familiarize my just over a year old Python with your Module.

Now in your examples you have bulb.py and this is using ""class BulbDevice(Device):"" I see you have two types A and B for different DPS. Now both the switch and lights work on the ""class OutletDevice(Device):"" for TreatLife and I can cycle both using the modified send_rawdps''_.py below.

bulb.py does print its tasks with no actions when I pounded out line 34 else: and 35 d.set_version(3.1) to get it to run as BulbDevice. Now when I use OutletDevice it fails when calling d.set_white() with an attributeError as this type probably needs to be added as type C?

Next is my snapshot.json and the switches have a "devId" and the name for my one and only switch that is "name": "Office Outside Lights". This should make it easier to get them separated as I am writing separate control polyglot Node Servers one for lighting and the other switches/dimmers. The lights do not have "devId" in their json listing between the "dps".

Now I am trying to build a modified wizard to bring in the DEVICEID, IP, KEY and the snapshot.py is a fantastic start I see you just added! So I will create the json, parse the data for the control of each switch and of course the token with requests.

Have one running as a Universal Devices Node Server and it is cycling a switch with manual inputs for its DEVICE key id & ip upon a token refresh every 20 minutes, but it is not working at all for actual control, lol.

I hope you do not mind my long winded message and hope to ask you some more questions along my weary path. I am 60 so it is kind of old dog with new tricks to keep my brain young. Sometime I get stuck on the simplest fundamentals mostly string formatting, lol! So for me to Len or separate out all of the switches from a json, then grab the data for DEVICE, then create another class to push that to for control, takes some brain twisting! What's that advertisement for jellyfish brain enhancers, Previgen? lol!

Highest Regards and Thank You for Your TIME!

Code Examples,

send_raw_dps_light_treatlife.py Light Bulb

import tinytuya
import time
import os
import random

DEVICEID = "BLAAAA"  
DEVICEIP = "BLAAAA" 
DEVICEKEY = "BLAAAA"   
DEVICEVERS = "us" ##This works? instead of 3.3 which they all are

# Check for environmental variables and always use those if available
DEVICEID = os.getenv("DEVICEID", DEVICEID)
DEVICEIP = os.getenv("DEVICEIP", DEVICEIP)
DEVICEKEY = os.getenv("DEVICEKEY", DEVICEKEY)
DEVICEVERS = os.getenv("DEVICEVERS", DEVICEVERS)

print("TinyTuya - Smart Bulb RGB Test [%s]\n" % tinytuya.__version__)
print('TESTING: Device %s at %s with key %s version %s' %
      (DEVICEID, DEVICEIP, DEVICEKEY, DEVICEVERS))

# Connect to the device - replace with real values
d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)
# Show status of device
data = d.status()

# Generate the payload to send - add all the DPS values you want to change here
payload1=d.generate_payload(tinytuya.CONTROL, {'20': False, '22': 100, '23': 10,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
time.sleep(2)
payload2=d.generate_payload(tinytuya.CONTROL, {'20': True, '22': 100, '23': 10,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
#time.sleep(2)
payload3=d.generate_payload(tinytuya.CONTROL, {'20': True, '22': 1000, '23': 236,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
# Send the payload to the device

d._send_receive(payload1)
time.sleep(2)
d._send_receive(payload2)
time.sleep(2)
d._send_receive(payload3)
#print('\nCurrent Status of Bulb: %r' % data)`

send_raw_dps_switch_treatlife.py Switch

import tinytuya
import time
import os
import random

DEVICEID = "BLAAAA"
DEVICEIP = "192.168.1.137"
DEVICEKEY = "BLAAAA"
DEVICEVERS = "us" ##This works? instead of 3.3 which they all are

# Check for environmental variables and always use those if available
DEVICEID = os.getenv("DEVICEID", DEVICEID)
DEVICEIP = os.getenv("DEVICEIP", DEVICEIP)
DEVICEKEY = os.getenv("DEVICEKEY", DEVICEKEY)
DEVICEVERS = os.getenv("DEVICEVERS", DEVICEVERS)

print("TinyTuya - Smart Bulb RGB Test [%s]\n" % tinytuya.__version__)
print('TESTING: Device %s at %s with key %s version %s' %
      (DEVICEID, DEVICEIP, DEVICEKEY, DEVICEVERS))

# Connect to the device - replace with real values
d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)

# Generate the payload to send - add all the DPS values you want to change here
#if data != [0]
#payload=d.generate_payload(tinytuya.CONTROL, {'1': False, '2': 50})
#time.sleep(2)
#payload=d.generate_payload(tinytuya.CONTROL, {'1': True, '2': 50})
#time.sleep(2)

payload1=d.generate_payload(tinytuya.CONTROL, {'1': False, '2': 50})
payload2=d.generate_payload(tinytuya.CONTROL, {'1': True, '2': 50})

# Send the payload to the device

d._send_receive(payload1)
time.sleep(2)
d._send_receive(payload2)

# Get the status of the device
#response = requests.request("GET", url, headers=headers, data=payload)

#print(str(d._send_receive(payload)))

#Command for 
# Show status of device
data = d.status()
print('\nCurrent Status of Bulb: %r' % data)

Here is my snapshot.json

{
    "timestamp": 1613548219.2819111,
    "devices": [
        {
            "name": "Under Cabinets",
            "ip": "192.168.1.158",
            "ver": "3.3",
            "id": "BLAAAAAAAAAAAAA",
            "key": "BLAAAAAAAAAAA",
            "dps": {
                "dps": {
                    "20": false,
                    "21": "scene",
                    "24": "007d007903e8",
                    "25": "05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000",
                    "26": 0
                }
            }
        },
        {
            "name": "Office Outside Lights",
            "ip": "192.168.1.137",
            "ver": "3.3",
            "id": "BLAAAAAAAAAA",
            "key": "BLAAAAAAAA",
            "dps": {
                "devId": "BLAAAAAAAAAA",
                "dps": {
                    "1": true,
                    "9": 0
                }
            }
        },
        {
            "name": "Garage",
            "ip": "192.168.1.138",
            "ver": "3.3",
            "id": "BLAAAAAAAAAA",
            "key": "BLAAAAAAAAAA",
            "dps": {
                "dps": {
                    "20": true,
                    "21": "colour",
                    "22": 1000,
                    "23": 767,
                    "24": "00ac02c501f0",
                    "25": "000e0d0000000000000000c803e8",
                    "26": 0
                }
            }
        },
        {
            "name": "Office Light",
            "ip": "192.168.1.139",
            "ver": "3.3",
            "id": "BLAAAAAAAAAA",
            "key": "BLAAAAAAAAAA",
            "dps": {
                "dps": {
                    "20": true,
                    "21": "white",
                    "22": 1000,
                    "23": 236,
                    "24": "00d60000026c",
                    "25": "000e0d0000000000000000c803e8",
                    "26": 0
                }
            }
        }
    ]
}

Lastly Postman Function list for the lights.

{
    "result": {
        "category": "dj",
        "functions": [
            {
                "code": "switch_led",
                "desc": "switch led",
                "name": "switch led",
                "type": "Boolean",
                "values": "{}"
            },
            {
                "code": "work_mode",
                "desc": "work mode",
                "name": "work mode",
                "type": "Enum",
                "values": "{\"range\":[\"white\",\"colour\",\"scene\",\"music\",\"scene_1\",\"scene_2\",\"scene_3\",\"scene_4\"]}"
            },
            {
                "code": "bright_value_v2",
                "desc": "bright value v2",
                "name": "bright value v2",
                "type": "Integer",
                "values": "{\"min\":10,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}"
            },
            {
                "code": "temp_value_v2",
                "desc": "temp value v2",
                "name": "temp value v2",
                "type": "Integer",
                "values": "{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":1000,\"step\":1}"
            },
            {
                "code": "colour_data_v2",
                "desc": "colour data v2",
                "name": "colour data v2",
                "type": "Json",
                "values": "{}"
            },
            {
                "code": "scene_data_v2",
                "desc": "scene data v2",
                "name": "scene data v2",
                "type": "Json",
                "values": "{}"
            },
            {
                "code": "music_data",
                "desc": "music data",
                "name": "music data",
                "type": "Json",
                "values": "{}"
            },
            {
                "code": "control_data",
                "desc": "control data",
                "name": "control data",
                "type": "Json",
                "values": "{}"
            },
            {
                "code": "countdown_1",
                "desc": "countdown 1",
                "name": "countdown 1",
                "type": "Integer",
                "values": "{\"unit\":\"\",\"min\":0,\"max\":86400,\"scale\":0,\"step\":1}"
            },
            {
                "code": "bright_value",
                "desc": "bright value",
                "name": "bright value",
                "type": "Integer",
                "values": "{\"min\":25,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}"
            },
            {
                "code": "temp_value",
                "desc": "temp value",
                "name": "temp value",
                "type": "Integer",
                "values": "{\"min\":0,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}"
            },
            {
                "code": "flash_scene_1",
                "desc": "flash scene 1",
                "name": "flash scene 1",
                "type": "Json",
                "values": "{\"h\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
            },
            {
                "code": "flash_scene_2",
                "desc": "flash scene 2",
                "name": "flash scene 2",
                "type": "Json",
                "values": "{\"h\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
            },
            {
                "code": "flash_scene_3",
                "desc": "flash scene 3",
                "name": "flash scene 3",
                "type": "Json",
                "values": "{\"h\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
            },
            {
                "code": "flash_scene_4",
                "desc": "flash scene 4",
                "name": "flash scene 4",
                "type": "Json",
                "values": "{\"h\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":360,\"step\":1},\"s\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1},\"v\":{\"min\":1,\"scale\":0,\"unit\":\"\",\"max\":255,\"step\":1}}"
            },
            {
                "code": "scene_select",
                "desc": "scene select",
                "name": "scene select",
                "type": "Enum",
                "values": "{\"range\":[\"1\",\"2\",\"3\",\"4\",\"5\"]}"
            },
            {
                "code": "read_time",
                "desc": "read time",
                "name": "read time",
                "type": "Integer",
                "values": "{\"unit\":\"minute\",\"min\":1,\"max\":60,\"scale\":0,\"step\":1}"
            },
            {
                "code": "rest_time",
                "desc": "rest time",
                "name": "rest time",
                "type": "Integer",
                "values": "{\"unit\":\"minute\",\"min\":1,\"max\":60,\"scale\":0,\"step\":1}"
            },
            {
                "code": "switch_health_read",
                "desc": "switch health read",
                "name": "switch health read",
                "type": "Boolean",
                "values": "{}"
            },
            {
                "code": "colour_data",
                "desc": "colour data",
                "name": "colour data",
                "type": "Json",
                "values": "{}"
            },
            {
                "code": "scene_data",
                "desc": "scene data",
                "name": "scene data",
                "type": "Json",
                "values": "{}"
            },
            {
                "code": "rhythm_mode",
                "desc": "rhythm mode",
                "name": "rhythm mode",
                "type": "Raw",
                "values": "{\"maxlen\":255}"
            },
            {
                "code": "wakeup_mode",
                "desc": "wakeup mode",
                "name": "wakeup mode",
                "type": "Raw",
                "values": "{\"maxlen\":255}"
            },
            {
                "code": "power_memory",
                "desc": "power memory",
                "name": "power memory",
                "type": "Raw",
                "values": "{\"maxlen\":255}"
            },
            {
                "code": "debug_data",
                "desc": "debug data",
                "name": "debug data",
                "type": "String",
                "values": "{\"maxlen\":255}"
            },
            {
                "code": "sleep_mode",
                "desc": "sleep mode",
                "name": "sleep mode",
                "type": "Raw",
                "values": "{\"maxlen\":255}"
            }
        ]
    },
    "success": true,
    "t": 1613549900843
}
jasonacox commented 3 years ago

Hi @sjpbailey - Thanks for this! You are right, the difference between the OutletDevice and BulbDevice class is the DPS mappings for those two different bulbs. This mapping is used for the Bulb functions like d.set_white() to change the correct DPS values. I would be interested in knowing what we need to change to support a Bulb C type.

Based on your notes above, it looks like all of your RGB lights are of the 3.3 device type:

Version 3.3 - Light Type (RGB)

DP ID Function Point Type Range Units
20 Switch bool True/False
21 Mode enum white,colour,scene,music
22 Bright integer 10-1000*
23 Color Temp integer 0-1000
24 Color hexstring h:0-360,s:0-1000,v:0-1000 hsv
25 Scene string n/a
26 Left time integer 0-86400 s
27 Music string n/a
28 Debugger string n/a
29 Debug string n/a

The snapshot.json data you have above (generate by python -m tinytuya scan?) looks like those devices map into the above unless I'm missing something. I do see you have one device "Office Outside Lights", that looks more like a switch or plug:

Version 3.3 - Plug, Switch, Power Strip Type

DP ID Function Point Type Range Units
1 Switch 1 bool True/False
9 Countdown 1 integer 0-86400 s

Is it really a bulb?

Looking at your code, it looks like you are basically doing this - do you need to use the payload method with both DPS 1 and 2 to get it to work?

d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)

# using payload method
payload=d.generate_payload(tinytuya.CONTROL, {'1': False, '2': 50})
d._send_receive(payload)

# this should do the same thing
data = d.set_status(False)

If you want to see debug information, you can add this to your python code (make sure you are using the latest tinytuya version):

tinytuya.set_debug(True)
sjpbailey commented 3 years ago

Hello Jason,

Yes, I used tinytuya for the snapshot xml. Yes, I am sure they are bulbs, lol!

I will put up a public repository to stay away from any crappy editing I would do on yours! Right now I am adding platform to my one pip module bascontrolns for Contemporary Controls DIY BACnet Devices (https://github.com/sjpbailey/bascontrol_ns)

As an apprentice developer I am used to hiding my junk in private, lmao.

I am building home automation interfaces as polyglot node servers and am dealing with Universal Devices ISY Java frontend using the polyinterface module.

I have a few node servers up on their store and the CEO has me tasked to bring TreatLife onto it, hey this is a free gig open Interop.

Thankfully I found all of Your hard work and followed setup with the API and was able to pull in the Postman data I needed to cycle the product locally. Without Your code I would have never gotten much further along than that!

I can hopefully give you any details on my end so you can add this product into your module.

If you have time down the road I can show you what I am doing because I am sure you can help me better understand how to get all of the switches and lights polled separately so I can get this done.

Now here in my code for the lights using the OutletDevice I am send the first payload to turn off the light, the the next two up the brightness and change the color or colour like your chart shows DPS 20 Switch, DPS 22 Bright DPS 23 Color Temp...

These are the only commands I have sent the bulbs and it cycles them off, dim with slight yellow then bright with a whiter color. I could not find any tables like your Verion 3.3 Light Type (RGB) on the API.

I also am just started to look at the under cabinet which is an LED strip they function similar to the bulbs and react to my hack code below. They also have some internal function because I just turned it on to see if I could override it and it is dancing around through its color on its own and is overridable.

Products Bulbs

  1. https://www.amazon.com/Treatlife-Multicolor-Compatible-Assistant-Equivalent/dp/B0839HV13X/ref=sr_1_2_sspa?dchild=1&keywords=Treatlife+led&qid=1613632789&s=hi&sr=1-2-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEzOVZOQUlWU0ZJSUImZW5jcnlwdGVkSWQ9QTA4MDkwODgyOVpBQTVIN0VESzlTJmVuY3J5cHRlZEFkSWQ9QTAxNjczODMxRlNQRTE2VVFMSlVXJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

LED Strip

  1. https://www.amazon.com/Treatlife-Changing-Backlight-Control-Bedroom/dp/B08HGQ7H8F/ref=sr_1_1_sspa?dchild=1&keywords=Treatlife+led+strip&qid=1613633253&s=hi&sr=1-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyMlNNS0haT1FVUEowJmVuY3J5cHRlZElkPUEwNjI0MTMzMjlQR1g0VkdDOEtZTyZlbmNyeXB0ZWRBZElkPUEwNjM4OTcyMUwyOU8xVDFBVVVPQiZ3aWRnZXROYW1lPXNwX2F0ZiZhY3Rpb249Y2xpY2tSZWRpcmVjdCZkb05vdExvZ0NsaWNrPXRydWU=

Highest Regards, Steve aka Bails

import tinytuya
import time
import os
import random

DEVICEID = "ebfc16d57ed374932cjqfk" #"ebfd4f4263bb769d99zjkq"  
DEVICEIP = "192.168.1.139" #"192.168.1.139" 
DEVICEKEY = "805217605357161b" #"ec0b2b581a246eab"   
DEVICEVERS = "us"

# Check for environmental variables and always use those if available
DEVICEID = os.getenv("DEVICEID", DEVICEID)
DEVICEIP = os.getenv("DEVICEIP", DEVICEIP)
DEVICEKEY = os.getenv("DEVICEKEY", DEVICEKEY)
DEVICEVERS = os.getenv("DEVICEVERS", DEVICEVERS)

print("TinyTuya - Smart Bulb RGB Test [%s]\n" % tinytuya.__version__)
print('TESTING: Device %s at %s with key %s version %s' %
      (DEVICEID, DEVICEIP, DEVICEKEY, DEVICEVERS))

# Connect to the device - replace with real values
d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)
# Show status of device
data = d.status()

# Generate the payload to send - add all the DPS values you want to change here
payload1=d.generate_payload(tinytuya.CONTROL, {'20': False, '22': 100, '23': 10,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
time.sleep(2)
payload2=d.generate_payload(tinytuya.CONTROL, {'20': True, '22': 100, '23': 10,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
#time.sleep(2)
payload3=d.generate_payload(tinytuya.CONTROL, {'20': True, '22': 1000, '23': 236,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
# Send the payload to the device

d._send_receive(payload1)
time.sleep(2)
d._send_receive(payload2)
time.sleep(2)
d._send_receive(payload3)
#print('\nCurrent Status of Bulb: %r' % data)
sjpbailey commented 3 years ago

LED Strip Status

TESTING: Device ebe097c0407da32084kvtr at 192.168.1.158 with key cb02297ceb692149 version us

Current Status of Bulb: {'dps': {'20': True, '21': 'scene', '24': '007d007903e8', '25': '05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000', '26': 0}}

Current Status of Bulb: {'dps': {'20': True, '21': 'scene', '24': '007d007903e8', '25': '05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000', '26': 0}}

Current Status of Bulb: {'dps': {'20': True, '21': 'scene', '24': '007d007903e8', '25': '05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000', '26': 0}}
sjpbailey commented 3 years ago

Light Bulb Status

TinyTuya - Smart Bulb RGB Test [1.1.3]

TESTING: Device ebfc16d57ed374932cjqfk at 192.168.1.139 with key 805217605357161b version us

Current Status of Bulb: {'dps': {'20': True, '21': 'white', '22': 1000, '23': 236, '24': '00d60000026c', '25': '000e0d0000000000000000c803e8', '26': 0}}

Current Status of Bulb: {'dps': {'20': True, '21': 'white', '22': 1000, '23': 236, '24': '00d60000026c', '25': '000e0d0000000000000000c803e8', '26': 0}}

Current Status of Bulb: {'dps': {'20': True, '21': 'white', '22': 1000, '23': 236, '24': '00d60000026c', '25': '000e0d0000000000000000c803e8', '26': 0}}
(3.7.7/envs/tiny_tuya_3.7.7_clean) stevenbailey@Stevens-iMac tinytuya-jan21-master %
sjpbailey commented 3 years ago

Jason, Here is just the test examples with one of your cloned repositories I have been playing with, it is not the most up to date but does have your up to date examples. I would work directly on yours but am not comfortable doing so at my level currently. I have a more for my UDI play. https://github.com/sjpbailey/treatlife-testing-tinytuya-jan21-master

creating a seperate snapshot for bulb and switch to test and will get back to you on that, the commands differ in Postman.

Switch

{
    "result": [
        {
            "code": "switch_1",
            "value": true
        },
        {
            "code": "countdown_1",
            "value": 0
        }
    ],
    "success": true,
    "t": 1613644145548
} 

Bulb

{
    "commands":[
        {
            "code": "switch_led",
            "value":true
        }
    ]
}
sjpbailey commented 3 years ago

Hey Jason,

Side tracked will get back at this soon in more detail studying you app init, I am just catching up to your hex string break down.

The Office Outside Lights is a switch I named it for my lights and should have included switch in its name when configuring it into the Api.

Highest Regards, Steve

On Feb 17, 2021, at 9:41 PM, Jason Cox notifications@github.com wrote:

Hi @sjpbailey https://github.com/sjpbailey - Thanks for this! You are right, the difference between the OutletDevice and BulbDevice class is the DPS mappings for those two different bulbs. This mapping is used for the Bulb functions like d.set_white() to change the correct DPS values. I would be interested in knowing what we need to change to support a Bulb C type.

Based on your notes above, it looks like all of your RGB lights are of the 3.3 device type:

Version 3.3 - Light Type (RGB)

DP ID Function Point Type Range Units 20 Switch bool True/False
21 Mode enum white,colour,scene,music
22 Bright integer 10-1000*
23 Color Temp integer 0-1000
24 Color hexstring h:0-360,s:0-1000,v:0-1000 hsv 25 Scene string n/a 26 Left time integer 0-86400 s 27 Music string n/a 28 Debugger string n/a 29 Debug string n/a The snapshot.json data you have above (generate by python -m tinytuya scan?) looks like those devices map into the above unless I'm missing something. I do see you have one device "Office Outside Lights", that looks more like a switch or plug:

Version 3.3 - Plug, Switch, Power Strip Type

DP ID Function Point Type Range Units 1 Switch 1 bool True/False
9 Countdown 1 integer 0-86400 s Is it really a bulb?

Looking at your code, it looks like you are basically doing this - do you need to use the payload method with both DPS 1 and 2 to get it to work?

d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY) d.set_version(3.3)

using payload method

payload=d.generate_payload(tinytuya.CONTROL, {'1': False, '2': 50}) d._send_receive(payload)

this should do the same thing

data = d.set_status(False) If you want to see debug information, you can add this to your python code (make sure you are using the latest tinytuya version):

tinytuya.set_debug(True) — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jasonacox/tinytuya/issues/34#issuecomment-781070578, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMRVB3JRZDNYT6EUFO5MUSDS7SR7DANCNFSM4XYENNTA.

sjpbailey commented 3 years ago

Jason,

Here is the Postman Python request code with the command string for the TreatLife bulbs.

I am not experienced enough to break it down for your advanced app but intend on searching it to find out how. Hoping to one day get there with experience!

Highest Regards, Steve

import requests

url = "https://openapi.tuyaus.com/v1.0/devices/ebfc16d57ed374932cjqfk/commands"

payload="\n{\n\t\"commands\":[\n\t\t{\n\t\t\t\"code\": \"bright_value_v2\",\n\t\t\t\"value\":1000\n },\n\t {\n\t\t\t\"code\": \"switch_led\",\n\t\t\t\"value\":true\n\n\t\t},\n\t {\n\t\t\t\"code\": \"temp_value_v2\",\n\t\t\t\"value\":100\n\n\t\t},\n\t {\n\t\t\t\"code\": \"work_mode\",\n\t\t\t\"value\":\"white\"\n\n\t\t}\n\t]\n}\n" headers = { 'client_id': 'txejpdfda9iwmn5cg2es', 'access_token': 'ff7f57ee0535b5a82961638398f00be4', 'sign': '702A5AD71949D8C4685C6C8C7BB8E2737645A92BFE6D1EFF1D3E0E81D8DE44A3', 't': '1613729594130', 'sign_method': 'HMAC-SHA256', 'Content-Type': 'application/json' }

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)

On Feb 17, 2021, at 9:41 PM, Jason Cox notifications@github.com wrote:

Hi @sjpbailey https://github.com/sjpbailey - Thanks for this! You are right, the difference between the OutletDevice and BulbDevice class is the DPS mappings for those two different bulbs. This mapping is used for the Bulb functions like d.set_white() to change the correct DPS values. I would be interested in knowing what we need to change to support a Bulb C type.

Based on your notes above, it looks like all of your RGB lights are of the 3.3 device type:

Version 3.3 - Light Type (RGB)

DP ID Function Point Type Range Units 20 Switch bool True/False
21 Mode enum white,colour,scene,music
22 Bright integer 10-1000*
23 Color Temp integer 0-1000
24 Color hexstring h:0-360,s:0-1000,v:0-1000 hsv 25 Scene string n/a 26 Left time integer 0-86400 s 27 Music string n/a 28 Debugger string n/a 29 Debug string n/a The snapshot.json data you have above (generate by python -m tinytuya scan?) looks like those devices map into the above unless I'm missing something. I do see you have one device "Office Outside Lights", that looks more like a switch or plug:

Version 3.3 - Plug, Switch, Power Strip Type

DP ID Function Point Type Range Units 1 Switch 1 bool True/False
9 Countdown 1 integer 0-86400 s Is it really a bulb?

Looking at your code, it looks like you are basically doing this - do you need to use the payload method with both DPS 1 and 2 to get it to work?

d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY) d.set_version(3.3)

using payload method

payload=d.generate_payload(tinytuya.CONTROL, {'1': False, '2': 50}) d._send_receive(payload)

this should do the same thing

data = d.set_status(False) If you want to see debug information, you can add this to your python code (make sure you are using the latest tinytuya version):

tinytuya.set_debug(True) — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jasonacox/tinytuya/issues/34#issuecomment-781070578, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMRVB3JRZDNYT6EUFO5MUSDS7SR7DANCNFSM4XYENNTA.

jasonacox commented 3 years ago

Impressive! I like your use of Postman to pull TuyaCloud data.

I noticed that you are using an older version of tinytuya (1.1.3), I would recommend using the latest (1.2.1) as we had a few bug fixes related to Bulbs since 1.1.3. pip install --upgrade tinytuya. It would be interesting to know if that changes anything.

Just so I can understand the current issue and desired outcome:

Can you point me to the specific changes you made to get these TreatLife devices to work? I can see if I can pull in the changes, or you can submit a merge request.

Thanks for your help!

sjpbailey commented 3 years ago

Hello Jason,

Thank You, lol! Apprentice working for you, the Sorcerer!

I have to get back to it, had a few pressing issues with some of my beta toys I thankfully cleared up today, pasting without double checking spaces got me again!

I will update my testing repository and look around at it some more Monday or today. I have a hybrid repository I put up temporarily, so I will get that in order and update to the latest, then re-test my process and debug some more. It is something I need to get done!

Digging Python, It is like a puzzle with convertible pieces or a crossword with a key in another language, keeps the brain young!

I will get back to you next week on progress!

Appreciate Your Valuable Time!

Highest Regards!

Issues:

  1. Can cycle switches with OutletDevice
  2. Bulbs would not work with your example bulb.py, however are manually changeable with the same class OutletDevice below.

Currently cycling bulbs and changes color, same as switches:

import tinytuya
import time
import os
import random

DEVICEID = "ebfc16d57ed374932cjqfk" #"ebfc16d57ed374932cjqfk" #"ebfd4f4263bb769d99zjkq"  
DEVICEIP = "192.168.1.142" #"192.168.1.139" #"192.168.1.139" 
DEVICEKEY = "805217605357161b" #"805217605357161b" #"ec0b2b581a246eab"   
DEVICEVERS = "us"

# Check for environmental variables and always use those if available
DEVICEID = os.getenv("DEVICEID", DEVICEID)
DEVICEIP = os.getenv("DEVICEIP", DEVICEIP)
DEVICEKEY = os.getenv("DEVICEKEY", DEVICEKEY)
DEVICEVERS = os.getenv("DEVICEVERS", DEVICEVERS)

print("TinyTuya - Smart Bulb RGB Test [%s]\n" % tinytuya.__version__)
print('TESTING: Device %s at %s with key %s version %s' %
      (DEVICEID, DEVICEIP, DEVICEKEY, DEVICEVERS))

# Connect to the device - replace with real values
d=tinytuya.OutletDevice(DEVICEID, DEVICEIP, DEVICEKEY)
d.set_version(3.3)
# Show status of device
data = d.status()

# Generate the payload to send - add all the DPS values you want to change here
payload1=d.generate_payload(tinytuya.CONTROL, {'20': False, '22': 100, '23': 10,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
time.sleep(2)
payload2=d.generate_payload(tinytuya.CONTROL, {'20': True, '22': 100, '23': 10,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
#time.sleep(2)
payload3=d.generate_payload(tinytuya.CONTROL, {'20': True, '22': 1, '23': 300,})
#time.sleep(1)
print('\nCurrent Status of Bulb: %r' % data)
# Send the payload to the device

d._send_receive(payload1)
time.sleep(2)
d._send_receive(payload2)
time.sleep(2)
d._send_receive(payload3)

#tinytuya.set_debug(True)
#print('\nCurrent Status of Bulb: %r' % data)
sjpbailey commented 3 years ago

Hey Jason,

Well, never went to bed.

The bulbs now work fine after I upgraded to your latest! Need to see what is different :'}.

Now we have one last issue with the light strip in the snapshot as Under Cabinets.

https://www.amazon.com/Treatlife-Changing-Backlight-Control-Bedroom/dp/B08HGQ7H8F/ref=asc_df_B08HGQ7H8F/?tag=hyprod-20&linkCode=df0&hvadid=459636115888&hvpos=&hvnetw=g&hvrand=7404133438160736088&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=9032295&hvtargid=pla-954658515006&psc=1

These led strips have a controller on its power plug modes are on, off and a run through scenes.

You can see compared to Office Light first on the snapshot has a different dps, also a response from postman for device information below.

Hope this helps.

Highest Regards!

Part of the snapshot.json for what is left Under cabinets, compared to a bulb, Office Light.

{
    "timestamp": 1613911967.4401228,
    "devices": [
        {
            "name": "Office Light",
            "ip": "192.168.1.142",
            "ver": "3.3",
            "id": "ebfc16d57ed374932cjqfk",
            "key": "805217605357161b",
            "dps": {
                "dps": {
                    "20": true,
                    "21": "white",
                    "22": 10,
                    "23": 65,
                    "24": "009c00b00123",
                    "25": "000e0d0000000000000000c803e8",
                    "26": 0
                }
            }
        },
        {
            "name": "Under Cabinets",
            "ip": "192.168.1.140",
            "ver": "3.3",
            "id": "ebe097c0407da32084kvtr",
            "key": "cb02297ceb692149",
            "dps": {
                "dps": {
                    "20": true,
                    "21": "colour",
                    "24": "009402f10183",
                    "25": "020e0d00001403e803e800000000",
                    "26": 0

Postman Response Under Cabinets Device Information:

{"result":{
    "active_time":1612504334,
    "biz_type":0,
    "category":"dd",
    "create_time":1612504334,
    "icon":"smart/icon/ay14906729310721fNjn/f266b748d1b92584887cde26ea9212ca.png",
    "id":"ebe097c0407da32084kvtr",
    "ip":"73.116.163.37",
    "lat":"37.71825745813938",
    "local_key":"cb02297ceb692149",
    "lon":"-121.470220678225",
    "name":"Under Cabinets ","online":true,
    "owner_id":"33161067",
    "product_id":"e8naaq4xcsfvuunf",
    "product_name":"LED Strip Lights",
    "status":[
        {
            "code":"switch_led",
            "value":true

        },
        {
            "code":"work_mode",
            "value":"white"

        },
        {
            "code":"colour_data",
            "value":"{\"h\":86,\"s\":0,\"v\":1000}"

        },
        {
            "code":"scene_data",
            "value":"{\"scene_num\":1,\"scene_units\":[{\"bright\":0,\"h\":46,\"s\":1000,\"temperature\":0,\"unit_change_mode\":\"static\",\"unit_gradient_duration\":13,\"unit_switch_duration\":14,\"v\":716}]}"

        },
        {
            "code":"countdown",
            "value":0}
    ],
    "sub":false,
    "time_zone":"-08:00",
    "uid":"az1610958067414WkfOO",
    "update_time":1613917945,
    "uuid":"c2854608edea94d7"},
    "success":true,
    "t":1613918472077

}
jasonacox commented 3 years ago

Hi @sjpbailey ! Thanks for the update. Good work.

What is happening with the "Under Cabinets" lights? Are you not able to control them at all or just some of the functions do not work? Let me know if you are seeing errors.

I see from the DPS values that the "Under Cabinets" lights do not have brightness and color temp settings. That seems logical since they are probably RGB only. That may mean that the bulb.py example would not be able to set them to white. I may consider adding that as a new bulb type (RGB only).

sjpbailey commented 3 years ago

Hey Jason.

Nothing wrong at all, Yes they would not cycle with bulb, as I was half asleep!

Thank You for the base to what I think I can now finish creating a Node Server for TreatLife on Universal devices ISY for home automation.

Is it okay to communicate with you in the future for some rookie questions? I will definitely not be a pest!

I and I am sure many really appreciate Your hard work! I would be nowhere without the short cuts you have provided for myself and many others! Wishing I had started Python in the 2.#'s, lol!

Highest Regards! Steve Bailey

sjpbailey commented 3 years ago

Hello Jason,

Hope you do not mind a progress report on my polyglot node server build.

I have learned so much working with Your code and have built an ugly switch separator from your beautiful code for the TreatLife product. It does function to separate switches from the lights I am working with so far. The switches use item 'devId' so I used that for now to separate them. This is only to plug into my polyglot Node server that is cycling one switch.

So this uses manually input api information which will be a parameter in my build and does work to grab token, then it polls devices to find switches based on item devId, then it cycles switches by name. This is where I will start my switch class(s) for my build a duplication of each with maybe a lambda function??

I have added one more switch on my bench for testing, this calls and writes an output.json and then cycles both switches by name. I added the second switch tonight naming both with switch to start, for another way to separate them from the lights, maybe string length in key is different also?

Anyway any suggestions to separate the switches would be helpful this does work for now.

Thank You for Your Hard and very nice work!!!

Highest Regards, Steve

Hackculator

>  ### Original Beautiful Code by Jason Cox #
> ### Code Hacked by Steve Bailey for a Polyglot Node Server on Universal Devices ISY #############
> 
> ####### Used to create controller and single node class for TreatLife product to polyinterface  #
> # This adds api and secert to custom parameters then polls Switches  Most in test Node Server   #
> 
> 
> # TinyTuya Switch Wizard
> # -*- coding: utf-8 -*-
> # Modules
> import requests
> import time
> import hmac
> import hashlib
> import json
> import pprint
> import logging
> import tinytuya
> # Backward compatability for python2
> try:
>     input = 'Yes'
> except NameError:
>     pass
> 
> def tuyaPlatform(apiRegion, apiKey, apiSecret, uri, token=None):
>     url = "https://openapi.tuya%s.com/v1.0/%s" % (apiRegion,uri)
>     now = int(time.time()*1000)
>     if(token==None):
>         payload = apiKey + str(now)
>     else:
>         payload = apiKey + token + str(now)
> 
>     # Sign Payload
>     signature = hmac.new(
>         apiSecret.encode('utf-8'),
>         msg=payload.encode('utf-8'),
>         digestmod=hashlib.sha256
>     ).hexdigest().upper()
> 
>     # Create Header Data
>     headers = {}
>     headers['client_id'] = apiKey
>     headers['sign_method'] = 'HMAC-SHA256'
>     headers['t'] = str(now)
>     headers['sign'] = signature
>     if(token != None):
>         headers['access_token'] = token
> 
>     # Get Token
>     response = requests.get(url, headers=headers)
>     try:
>         response_dict = json.loads(response.content.decode())
>     except:
>         try:
>             response_dict = json.loads(response.content)
>         except:
>             print("Failed to get valid JSON response")
> 
>     return(response_dict)
> 
> def wizard(color=True):
> ##### This is already in Node Sever for Custom Parameters for api Information below ###########
> # 
> ##### Change All print statements to LOGGER.info() ############################################    
>     # Get Configuration Data
>     #CONFIGFILE = 'tinytuya.json'
>     #DEVICEFILE = #'devices.json'
>     SNAPSHOTFILE = 'output.json'
>     config = {}
>     config['apiKey'] = "txejpdfda9iwmn5cg2es"
>     config['apiSecret'] = "46d6072ffd724e0ba5ebeb5cc6b9dce9"
>     config['apiRegion'] = 'us'
>     config['apiDeviceID'] = "017743508caab5f0973e"
>     needconfigs = True
> ##### Loads as custom parameters in Node Server ##############################################
>     ##try:
>         # Load defaults
>     ##    with open(CONFIGFILE) as f:
>     ##        config = json.load(f)
>    ## except:
>         # First Time Setup
>     ##    pass
>     
>     print('')
>     print('Hello Polling Switches')
>     print('') 
>     print('TinyTuya Switch Discoverer Bailey Hack' + ' [%s]' % (tinytuya.version))
>     print('')
> 
>     if(config['apiKey'] != '' and config['apiSecret'] != '' and
>             config['apiRegion'] != '' and config['apiDeviceID'] != ''):
>         needconfigs = False
>         answer = 'Y' #input(subbold + '    Use existing credentials ' +
>                   #     normal + '(Y/n): ')
>         if('Y'[0:1].lower() == 'n'):
>             needconfigs = True
> 
>     
>     KEY = config['apiKey']
>     SECRET = config['apiSecret']
>     DEVICEID = config['apiDeviceID']
>     REGION = config['apiRegion']        # us, eu, cn, in
>     LANG = 'us'                         # en or zh
> 
>     # Get Oauth Token from tuyaPlatform
>     uri = 'token?grant_type=1'
>     response_dict = tuyaPlatform(REGION, KEY, SECRET,uri)
>     token = response_dict['result']['access_token']
> 
>     # Get UID from sample Device ID 
>     uri = 'devices/%s' % DEVICEID
>     response_dict = tuyaPlatform(REGION, KEY, SECRET, uri, token)
>     uid = response_dict['result']['uid']
> 
>     # Use UID to get list of all Devices for User
>     uri = 'users/%s/devices' % uid
>     json_data = tuyaPlatform(REGION, KEY, SECRET, uri, token)
>  
>     # Filter to only Name, ID and Key
>     tuyadevices = []
>     for i in json_data['result']:
>         item = {}
>         item['name'] = i['name'].strip()
>         item['id'] = i['id']
>         item['key'] = i['local_key']
>         tuyadevices.append(item)
> 
>     # Display device list
>     ##print("\n\n" + bold + "Device Listing\n" + dim)
>     ##output = json.dumps(tuyadevices, indent=4)  # sort_keys=True)
>     ##print(output)
> 
>     # Save list to devices.json
>     ##print(bold + "\n>> " + normal + "Saving list to " + DEVICEFILE)
>     ##with open(DEVICEFILE, "w") as outfile:
>     ##    outfile.write(output)
>     ##print(dim + "    %d registered devices saved" % len(tuyadevices))
> 
> ######################### Device Switch Poller #################################
> # 
> # Using devId to poll Switches without lights or should i use key length??????#$$
> #
> # NEED TO GRAB SWITCH DEVICES AND MAKE THEM SEPARATE NODES FOR EACH SWITCH $$$$$$
> #
> ######################### By-Pass Data Input ########################$$$$$$$$$$$$
>     # Find out if we should poll all devices
>     #answer = 'Yes' #input(subbold + '\nPoll local devices? ' +
>               #     normal + '(Y/n): ')
>     if('Y'[0:1].lower() != 'n'):
>         # Scan network for devices and provide polling data
>         ###print(normal + "\nScanning local network for Tuya devices...")
>         devices = tinytuya.deviceScan(False, 20) #### changed 20 to 1
>         ##print("    %s%s local devices discovered%s" %
>         ##      ( len(devices)))
>         ##print("")
> 
>         def getIP(d, gwid):
>             for ip in d:
>                 if (gwid == d[ip]['gwId']):
>                     return (ip, d[ip]['version'])
>             return (0, 0)
> 
>         polling = []
>         print("Polling Switch devices...")
>         for i in tuyadevices:
>             item = {}
>             name = i['name']
>             (ip, ver) = getIP(devices, i['id'])  ## 'id'
>             item['name'] = name
>             item['ip'] = ip
>             item['ver'] = ver
>             item['id'] = i['id']
>             item['key'] = i['key']
>             if (ip == 0):
>                 #print("    %s[%s] - %s%s - %sError: No IP found%s" %
>                 #      (subbold, name, dim, ip, alert, normal))
>                 pass
>             else:
>                 try:
>                     d = tinytuya.OutletDevice(i['id'], ip, i['key'])
>                     if ver == "3.3":
>                         d.set_version(3.3)
>                     data = d.status()
>                     if 'dps' in data:
>                         item['devId'] = data
>                         #state = alertdim + "Off" + dim
>                         try:
>                             if '1' in data['devId'] or 'devId' in data['devId']:
>                                 #state = bold + "On" + dim
>                                 #print("    %s[%s] - %s%s - %s - DPS: %r" %
>                                     #(name, ip, state, data['dps']))
>                                 print("%-35.35s %-24s %-16s %-17s %-5s" % (
>                                     item["name"],
>                                     item["id"],
>                                     item["ip"],
>                                     item["key"],
>                                     item["ver"]))
>                             else:
>                                 print("    %s[%s] - %s%s - DPS: %r" %
>                                     (name, ip, data['devId']))
>                                 pass
>                         except:
>                             pass
>                     else:
>                         pass
>                 except:
>                     pass
>                     #print("    %s[%s] - %s%s - %sNo Response" %
>                     #      (name, ip, alertdim))
>             polling.append(item)
>         # for loop
> ###################################################### Need to Clean this up ###################################################
>         # Save polling data snapsot
>         current = {'timestamp' : time.time(), 'devices' : polling}
>         output = json.dumps(current, indent=4)
>         print(name)
> 
>         #print( "\n>> "  + "Saving device snapshot data to " + SNAPSHOTFILE)
>         with open(SNAPSHOTFILE, "w") as outfile:       #with open(SNAPSHOTFILE, "w") as outfile:
>             outfile.write(output)                      #outfile.write(output)   outfile.read(int(output))
>         
> 
> ######################################################## NODE SWITCH CLASS START HERE  #########################################
> 
> #Class switchNode(self, SNAPSHOT PASSED ############)      
>         ######### PULLING FROM FILE ############
>         with open(SNAPSHOTFILE) as json_file:   # 'snapshot.json'
>         
>             data = json.load(json_file)   # current gives error output try loads instead of load data = json.load(json_file)
>                                           # output 
> 
> ######################################################## NODE SWITCH CLASS START HERE  #########################################
> 
> #class SwitchNodes(polyinterface.Node):
>     #def __init__(self, controller, primary, address, name): #, ip, id1, key1
>     #    super(SwitchNodes, self).__init__(controller, primary, address, name)
> 
> ######################## SEPERATE OUT THE SWITCHES THEY HAVE "devId" IN BETWEEN 'DPS' or should I use length???? ############## 
>         
>         print("\nSwitch Passed Parameters\n")
>         print(name)
>         print(ip)
>         print(item["id"])
>         print(item["key"])
>         
>         print("\nSwitch Status\n")
>         #print(data)
>         print("")
>         #print(devices)
> 
>                     
>         # Turn on a device by name
>         def turn_on(name):
>             # find the right item that matches name
>             for item in data["devices"]:
>                 if item["name"] == name:
>                     break
>             print("\nTurning On: %s" % item["name"])
>             d = tinytuya.OutletDevice(item["id"], item["ip"], item["key"])
>             d.set_version(float(item["ver"]))
>             d.set_status(True)
> 
>              # Turn off a device by name
>         def turn_off(name):
>             # find the right item that matches name
>             for item in data["devices"]:
>                 if item["name"] == name:
>                     break
>             print("\nTurning Off: %s" % item["name"])
>             d = tinytuya.OutletDevice(item["id"], item["ip"], item["key"])
>             d.set_version(float(item["ver"]))
>             d.set_status(False)
>         # Test it
>         turn_off('Switch Family Room Sconces') #Switch Family Room Sconces #Switch Office Outside Lights
>         time.sleep(2)
>         turn_on('Switch Family Room Sconces') #Switch Family Room Sconces #Switch Office Outside Lights
>         time.sleep(2)
>         turn_off('Switch Office Outside Lights') #Switch Family Room Sconces #Switch Office Outside Lights
>         time.sleep(2)
>         turn_on('Switch Office Outside Lights') #Switch Family Room Sconces #Switch Office Outside Lights
>     
>     print("\nDone.\n")
>     return
> 
> if __name__ == '__main__':
> 
>     try:
>         wizard()
>     except KeyboardInterrupt:
>         pass
> 

.json file produced

{
    "timestamp": 1615017609.8556108,
    "devices": [
        {
            "name": "Switch Family Room Sconces",
            "ip": "192.168.1.160",
            "ver": "3.3",
            "id": "017743508caab5f385a7",
            "key": "7b8f2415ac96dfea",
            "devId": {
                "devId": "017743508caab5f385a7",
                "dps": {
                    "1": true,
                    "9": 0
                }
            }
        },
        {
            "name": "Under Cabinets",
            "ip": "192.168.1.146",
            "ver": "3.3",
            "id": "ebe097c0407da32084kvtr",
            "key": "cb02297ceb692149",
            "devId": {
                "dps": {
                    "20": true,
                    "21": "white",
                    "24": "013c03a403a1",
                    "25": "020e0d00001403e803e800000000",
                    "26": 0
                }
            }
        },
        {
            "name": "Garage",
            "ip": "192.168.1.142",
            "ver": "3.3",
            "id": "ebfd4f4263bb769d99zjkq",
            "key": "ec0b2b581a246eab",
            "devId": {
                "dps": {
                    "20": true,
                    "21": "white",
                    "22": 1000,
                    "23": 560,
                    "24": "0071037501bf",
                    "25": "04464602007803e803e800000000464602007803e8000a00000000",
                    "26": 0
                }
            }
        },
        {
            "name": "Switch Office Outside Lights",
            "ip": "192.168.1.139",
            "ver": "3.3",
            "id": "017743508caab5f0973e",
            "key": "e779c96c964f71b2",
            "devId": {
                "devId": "017743508caab5f0973e",
                "dps": {
                    "1": true,
                    "9": 0
                }
            }
        },
        {
            "name": "Office Light",
            "ip": "192.168.1.141",
            "ver": "3.3",
            "id": "ebfc16d57ed374932cjqfk",
            "key": "805217605357161b",
            "devId": {
                "dps": {
                    "20": true,
                    "21": "white",
                    "22": 208,
                    "23": 0,
                    "24": "00b4021603d0",
                    "25": "000e0d0000000000000000c803e8",
                    "26": 0
                }
            }
        }
    ]
}

Responce

Hello Polling Switches

TinyTuya Switch Discoverer [1.2.2]

Polling Switch devices... Switch Family Room Sconces 017743508caab5f385a7 192.168.1.160 7b8f2415ac96dfea 3.3
Switch Office Outside Lights 017743508caab5f0973e 192.168.1.139 e779c96c964f71b2 3.3
Office Light

Switch Passed Parameters

Office Light 192.168.1.141 ebfc16d57ed374932cjqfk 805217605357161b

Switch Status

Turning Off: Switch Family Room Sconces

Turning On: Switch Family Room Sconces

Turning Off: Switch Office Outside Lights

Turning On: Switch Office Outside Lights

Done.

I am working on the passed Parameters also as you can see the last item in output.json is up first. So I need to get my separator functioning and pushing out each switch only.

sjpbailey commented 3 years ago

Hello Jason, Here is what I have assembled from your code to poll switches and a separate py to poll lights for the TreatLife product that also switches them for testing. And If you edit line 171 to ((if '20' in data['dps'] or '1' in data['devId']:)) you will get all devices.

I still have to fix the print line for Authentication lol! what is it key, token, refresh token?

I have not completely tested it to cycle lights because I am working on switches currently.

These for now only cycles the last item on the json, and the json is turned if you use the phone app to turn a device on or off(?). If you have a minute can you Please take a look and maybe you can suggest a way to be able to pick which is cycled, instead of last on the json??? PLEASE because as you can see I am in way over my head! lol!

I am using this to build a polyglot node server for home automation and am close to getting it together. It just combines your code to Authenticate token, then poll devices, then the polyinterface module I am working with will poll for token every 20 minutes this will also update DHCP ip addresses so they can be found with their name, id, ip, and key, well IP anyway lol.

I have learned so much from your work!!! THANK YOU!!!!!

Highest Regards!!!

Switches (https://github.com/sjpbailey/treatlife-testing-tinytuya-jan21-master/blob/master/Controller-polls-switch-only-dirty-cycles-by-DEV-clean-for-Switch-NS.py)

Lights (https://github.com/sjpbailey/treatlife-testing-tinytuya-jan21-master/blob/master/Controller-polls-switch-only-dirty-cycles-by-DEV-clean-for-Light-NS.py)

Result run example of switch:

TreatLife Device Discovery

Authentication [1.2.2]
API, Key, Token, Refresh, Payload  txejpdfda9iwmn5cg2es1615792159869
API, Key, Token, Refresh, Payload  txejpdfda9iwmn5cg2es86c7953fe742a82e12f0ab6117c30ebe1615792160078
API, Key, Token, Refresh, Payload  txejpdfda9iwmn5cg2es86c7953fe742a82e12f0ab6117c30ebe1615792160276

Polling TreatLife Devices...

EACH TREATLIFE SWITCH TO NODE WITH ADDNODE FROM HERE!!!
Switch Office Outside Lights        017743508caab5f0973e     192.168.1.146    e779c96c964f71b2  3.3  

EACH TREATLIFE SWITCH TO NODE WITH ADDNODE FROM HERE!!!
Switch Family Room Sconces          017743508caab5f385a7     192.168.1.147    7b8f2415ac96dfea  3.3  

Currently Passed Name: Switch Family Room Sconces
Currently Passed ID: 017743508caab5f385a7
Currently Passed IP: 192.168.1.147
Currently Passed KEY: 7b8f2415ac96dfea

TESTING NODE Switch by DEVICE:  Switch Family Room Sconces 017743508caab5f385a7 at 192.168.1.147 with key 7b8f2415ac96dfea version us

Test Cycle Switch by DEVICE ON

Current Status of Switch Family Room Sconces Switch: {'devId': '017743508caab5f385a7', 'dps': {'1': True, '9': 0}}

Test Cycle Switch by DEVICE OFF

Current Status of Switch Family Room Sconces Switch: {'devId': '017743508caab5f385a7', 'dps': {'1': True, '9': 0}}

Test Cycle Switch by Name 

Turning Off: Switch Family Room Sconces

Current Status of Switch Family Room Sconces Switch: {'devId': 'devId', 'dps': {'1': True, '9': 0}}

Turning On: Switch Family Room Sconces

Current Status of Switch Family Room Sconces Switch: {'devId': 'devId', 'dps': {'1': True, '9': 0}}

Turning Off: Switch Family Room Sconces

Current Status of Switch Family Room Sconces Switch: {'devId': 'dps', 'dps': {'1': True, '9': 0}}

Turning On: Switch Family Room Sconces

Current Status of Switch Family Room Sconces Switch: {'devId': 'dps', 'dps': {'1': True, '9': 0}}

Switch Family Room Sconces
017743508caab5f385a7
192.168.1.147
7b8f2415ac96dfea

Done.
sjpbailey commented 2 years ago

Hey Jason!

I finally have this somewhat working for Universal Devices automation using TreatLife switches and LED lights(https://youtu.be/ypp4E7ZSI20), (https://youtu.be/iZae6PAbMYQ). Working on the LED Strips and gathering json for local devices.

Here is a local device sorter for snapshot.json: (https://github.com/sjpbailey/udi-poly-tuya-python-master-v3/blob/master/Test-tuya/local_tuya_device_json_sort_addNode.py)

Right now I am grabbing local devices with snapshot.jason this monster: (https://github.com/sjpbailey/udi-poly-tuya-python-master-v3/blob/master/nodes/tuya-device-json-grabber.py). I would like to get this into one function or get it to run in the polyglot app!

Full repository here: (https://github.com/sjpbailey/udi-poly-tuya-python-master-v3) need to do a README.

I have been accused of being unethical re-working your code as a sudo module by my competition? I am always here to help as this has all been a hobby as I am making 10% of nothing for all of my input so far and hope to have it make money on their store. Wish me luck as this will hopefully bring us some capital to split in the near future.

Highest Regards!!!

jasonacox commented 2 years ago

Nice work, Steven! Thanks for the update, and yes, good luck! :)