Android Question Tuya API convert from python - Error

GJREDITOR

Member
I am trying to control a WIPRO smart bulb which is registered in "Smart Life" app (basically Tuya app) . The following python code works:
python code to discover and turn on and off WIPRO bulb:
import requests
import pprint
import time

print("GET AUTH-TOKEN")
auth = requests.post(
    "https://px1.tuyaus.com/homeassistant/auth.do",
    data={
        "userName": "[email protected]",
        "password": "apppassword",
        "countryCode": "1",
        "bizType": "smart_life",
        "from": "tuya",
    },
).json()
pprint.pprint(auth)
access_token = auth["access_token"]
print(">> ACCESS_TOKEN=" + access_token)

print("GET DEVICES")
devices = requests.post(
    "https://px1.tuyaus.com/homeassistant/skill",
    json={"header": {"name": "Discovery", "namespace": "discovery", "payloadVersion": 1}, "payload": {"accessToken": access_token}}
).json()
pprint.pprint(devices)
bell_device = next(dev for dev in devices["payload"]["devices"] if "WIPRO" in dev["name"])
bell_id = bell_device["id"]
print(">> BELL_ID=" + bell_id)

print("TURNING ON")
turnon = requests.post(
    "https://px1.tuyaus.com/homeassistant/skill",
    json={"header": {"name": "turnOnOff", "namespace": "control", "payloadVersion": 1}, "payload": {"accessToken": access_token, "devId": bell_id, "value":"1"}}
).json()
pprint.pprint(turnon)

print("SLEEP FOR 10 SECONDS")
time.sleep(10)

print("TURNING OFF")
turnoff = requests.post(
    "https://px1.tuyaus.com/homeassistant/skill",
    json={"header": {"name": "turnOnOff", "namespace": "control", "payloadVersion": 1}, "payload": {"accessToken": access_token, "devId": bell_id, "value":"0"}}
).json()
pprint.pprint(turnoff)

In B4X, as a first step, I am trying to discover devices. I translated the above python code as follows:
B4X code to discover devices registered to smart life app:
Sub GetDevices(accessToken As String)
    Log($"The Access Token is:${accessToken}"$)
    Dim job As HttpJob
    job.Initialize("client",Me)
    
    Dim data As Map
    data.Initialize
    data.Put("header", CreateMap("name": "Discovery", "namespace": "discovery", "payloadVersion": 1))
    data.Put("payload", CreateMap("accessToken": accessToken))
    
    job.PostString("https://px1.tuyaus.com/homeassistant/skill",$"${data}"$)
    Log($"${data}"$)
    Wait For (job) JobDone(job As HttpJob)
    If job.Success Then
        Log(job.GetString)
        Dim devices As Map
        devices = JsonToMap(job.GetString)
        Log($"{devices}"$)
        lbl_devices.Text =$"${devices}"$
    Else
        Log("Failed to send message. Error: " & job.ErrorMessage)
    End If
    job.Release
End Sub
However I don't get any device in log . The output of Log(job.GetString) is:
{"payload":{},"header":{"code":"DependentServiceUnavailable","payloadVersion":1}}
Please let me know where is the error.
 
Solution
As the b4x code for Tuya doesn't work
Try setting the content type for the B4X code:
B4X:
'...
    job.PostString("https://px1.tuyaus.com/homeassistant/skill",jsonData)
    job.GetRequest.SetContentType("application/json") ' Set the proper content type, so that the server knows what it is receiving
    Wait For (job) JobDone(job As HttpJob)
'...

Update: I'm pretty sure that this is the answer/solution since the Tuya API docs state the following:
When the request method POST is used, the Content-Type parameter must be set to application/json.
Link: https://developer.tuya.com/en/docs/iot/api-request?id=Ka4a8uuo1j4t4

OliverA

Expert
Licensed User
Longtime User
You never converter your Map named data into a JSON string.
 
Upvote 0

GJREDITOR

Member
You never converter your Map named data into a JSON string.
Thank you for pointing out but I still don't get any devices:
Modified code to get devices : json.ToString:
Sub GetDevices(accessToken As String)
    Log($"The Access Token is:${accessToken}"$)
    Dim job As HttpJob
    job.Initialize("client",Me)
    Dim data As Map
    data.Initialize
    data.Put("header", CreateMap("name": "Discovery", "namespace": "discovery", "payloadVersion": 1))
    data.Put("payload", CreateMap("accessToken": accessToken))
    Dim json As JSONGenerator
    json.Initialize(data)
    Dim jsonData As String
    jsonData = json.ToString
    Log($"Json data is : ${jsonData}"$)
    job.PostString("https://px1.tuyaus.com/homeassistant/skill",jsonData)

    Wait For (job) JobDone(job As HttpJob)
    If job.Success Then
        Log(job.GetString)
       Dim devices As Map
        devices = JsonToMap(job.GetString)
        Log($"{devices}"$)
        lbl_devices.Text =$"${devices}"$
    Else
        Log("Failed to send message. Error: " & job.ErrorMessage)
    End If
    job.Release
End Sub

I tried the same url and json in https://reqbin.com/ and got the expected result:
Devices JSON output:
{

    "payload": {

        "devices": [{

            "data": {

                "brightness": "230",

                "color_mode": "colour",

                "online": false,

                "state": "false",

                "color_temp": 4882

            },

            "icon": "https://images.tuyain.com/smart/solution/143006/37e69c2be34b7642_cover.png",

            "name": "WIPRO RGBCW_ SMART ",

            "id": "xxxxx1da4388d0f00dxxxxx",

            "dev_type": "light",

            "ha_type": "light"

        }, {

            "data": {},

            "name": "remote On",

            "id": "xxxxrsjG1HUIxxxx",

            "dev_type": "scene",

            "ha_type": "scene"

        }],

        "scenes": []

    },

    "header": {

        "code": "SUCCESS",

        "payloadVersion": 1

    }

}

I also got the expected result in Google App Script
There is even a webpage which controls the devices (https://smartathome.co.uk/smartlife/index.html).
So is there something additional that needs to be set in b4x ?
 
Upvote 0

GJREDITOR

Member
Hi
As the b4x code for Tuya doesn't work, I am trying to run below java script in webview. However, I get no response from the button. Can someone please let me know what needs to be done? Thank you.
java script which works:
html java script to turn on and off the smart bulb:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Smart Bulb Controller</title>
</head>
<body>
    <h1>Smart Bulb Controller</h1>
    <button id="turnOnButton">Turn On Bulb</button>
    <button id="turnOffButton">Turn Off Bulb</button>

    <script>
        // javaScript code ..
        const url = 'https://px1.tuyaus.com/homeassistant/skill';
        const bulbId = 'd7e1da4388d0f00dcexnm7'; // Bulb's ID
        const accessToken = 'INv051h5f7c3bc0in1697945905900oeqpA2XFpXXXXX'; // Access token

        const turnOnData = {
            payload: {
                devId: bulbId,
                accessToken: accessToken,
                value: '1',
            },
            header: {
                payloadVersion: 1,
                name: 'turnOnOff',
                namespace: 'control',
            },
        };

        const turnOffData = {
            payload: {
                devId: bulbId,
                accessToken: accessToken,
                value: '0',
            },
            header: {
                payloadVersion: 1,
                name: 'turnOnOff',
                namespace: 'control',
            },
        };

        // Function to send request to turn the bulb on or off
        async function controlBulb(data) {
            try {
                const response = await fetch(url, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify(data),
                });
                const text = await response.text();
                console.log(text);
            } catch (error) {
                console.error('Error:', error);
            }
        }

        // Event listener for the button press to turn the bulb on
        document.getElementById('turnOnButton').addEventListener('click', () => {
            controlBulb(turnOnData);
        });

        // Event listener for the button press to turn the bulb off
        document.getElementById('turnOffButton').addEventListener('click', () => {
            controlBulb(turnOffData);
        });
    </script>
</body>
</html>

B4X Webview Code
b4x webview code:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Private WebView1 As WebView
End Sub

Public Sub Initialize
'    B4XPages.GetManager.LogEvents = True
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
End Sub

'You can see the list of page related events in the B4XPagesManager object. The event name is B4XPage.

Private Sub Button1_Click
    WebView1.Loadurl($"${xui.FileUri(File.DirAssets, "bulbonoff.html")}"$)
End Sub
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
As the b4x code for Tuya doesn't work
Try setting the content type for the B4X code:
B4X:
'...
    job.PostString("https://px1.tuyaus.com/homeassistant/skill",jsonData)
    job.GetRequest.SetContentType("application/json") ' Set the proper content type, so that the server knows what it is receiving
    Wait For (job) JobDone(job As HttpJob)
'...

Update: I'm pretty sure that this is the answer/solution since the Tuya API docs state the following:
When the request method POST is used, the Content-Type parameter must be set to application/json.
Link: https://developer.tuya.com/en/docs/iot/api-request?id=Ka4a8uuo1j4t4
 
Last edited:
Upvote 1
Solution

GJREDITOR

Member
Try setting the content type for the B4X code:
B4X:
'...
    job.PostString("https://px1.tuyaus.com/homeassistant/skill",jsonData)
    job.GetRequest.SetContentType("application/json") ' Set the proper content type, so that the server knows what it is receiving
    Wait For (job) JobDone(job As HttpJob)
'...

Update: I'm pretty sure that this is the answer/solution since the Tuya API docs state the following:

Link: https://developer.tuya.com/en/docs/iot/api-request?id=Ka4a8uuo1j4t4
Thank you soooo much . That was what was missing:) Works after setting the content type.
 
Upvote 0

Ilya G.

Active Member
Licensed User
Longtime User
I don’t understand how to send authorization? python code with my credentials works

B4X:
Dim job As HttpJob
job.Initialize("", Me)
job.PostString("https://px1.tuyaeu.com/homeassistant/auth.do", $"userName=&password=&countryCode="1"&bizType=tuya&from=tuya"$)
job.GetRequest.SetContentType("application/x-www-form-urlencoded")

{"responseStatus":"error","errorMsg":"Get accesstoken failed. Username or password error!"}

B4X:
Dim data As Map
data.Initialize
data.Put("userName", "")
data.Put("password", "")
data.Put("countryCode", "1")
data.Put("bizType", "tuya")
data.Put("from", "tuya")
Dim json As JSONGenerator
json.Initialize(data)
Dim job As HttpJob
job.Initialize("", Me)
job.PostString("https://px1.tuyaeu.com/homeassistant/auth.do", json.ToString)
job.GetRequest.SetContentType("application/json")

{"responseStatus":"error","errorMsg":"Get accesstoken failed. Invalid parms."}
 
Last edited:
Upvote 0
Top