Trouble with Tuya API POST request returning code 1004 or sign invalid

Developer Platform features usage and feedback.


Post Reply
mark_libres
Posts: 1

I'm working on integrating the Tuya API into my Python application to control smart devices. I'm able to successfully make GET requests, but I'm encountering issues when trying to make POST requests.

Whenever I attempt to send a POST request to the Tuya API, I consistently receive either a code 1004 error or a "sign invalid" message. I've verified that my client ID and client secret are correct, and my GET requests are working fine.

here is my Request class file

Code: Select all

import time
import hashlib
import hmac
import requests
import subprocess

class Request:

def __init__(self):

    self.ClientID = "**"
    self.ClientSecret = "**"
    self.BaseUrl = "https://openapi.tuyaus.com"
    self.EmptyBodyEncoded = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"


def headers(self, sign):
    
    return {
        "sign_method": "HMAC-SHA256",
        "client_id": self.ClientID,
        "t": self.tuyatime(),
        "mode": "cors",
        "Content-Type": "application/json",
        "sign": sign
    }



def tuyatime(self):
    return str(int(time.time() * 1000))


def access_token(self):
    URL = "/v1.0/token?grant_type=1"
    StringToSign = f"{self.ClientID}{self.tuyatime()}GET\n{self.EmptyBodyEncoded}\n\n{URL}"
    AccessTokenSign = hmac.new(self.ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

    AccessTokenResponse = requests.get(f"{self.BaseUrl}{URL}", headers=self.headers(AccessTokenSign))
    return AccessTokenResponse.json()['result']["access_token"]

    pass


def sign(self, url, method):

    access_token = self.access_token()
    str_sign = f"{self.ClientID}{access_token}{self.tuyatime()}{method}\n{self.EmptyBodyEncoded}\n\n{url}"
    
    return [
        hmac.new(self.ClientSecret.encode(), str_sign.encode(), hashlib.sha256).hexdigest().upper(),
        access_token
    ]



def _sign(self, url, method, data):

    # Define the body

    print("data >> ", data)
    access_token = self.access_token()
    encoded_body = self.EmptyBodyEncoded
    
    if data != None:
        body_str = str(data).encode('utf-8')
        encoded_body = hashlib.sha256(body_str).hexdigest()


    print("encoded_body >> ", encoded_body)
    
    stringToSign = "\n".join([
        method, #HTTPMethod
        encoded_body, #Content-SHA256
        "", #Optional_Signature_key
        url #URL
    ])


    str_sign = f"{self.ClientID}{access_token}{self.tuyatime()}{stringToSign}"

    
    return [
        hmac.new(self.ClientSecret.encode(), str_sign.encode(), hashlib.sha256).hexdigest().upper(),
        access_token
    ]


def request_extra_params(self, url, method, data):

    sign, access_token = self._sign(url, method, data)

    headers = self.headers(sign)
    headers["access_token"] = access_token

    return {
        "headers" : headers
    }

    pass

def get(self, url, **kwargs):
    extra_params = self.request_extra_params(url, "GET", None)
    return requests.get(f"{self.BaseUrl}{url}", **extra_params, **kwargs)

def post(self, url, data, **kwargs):

    extra_params = self.request_extra_params(url, "POST", data)
    return requests.post(f"{self.BaseUrl}{url}", json=data, **extra_params, **kwargs)
    

here is the common file

Code: Select all

from classes.Request import Request
import json

_request = Request()

DEVICES = {
    "mark_table" : "***"
}


def set_state_mark_table(value = True):
    URL = f"/v1.0/devices/{DEVICES['mark_table']}/commands"
    return _request.post(URL, data=json.dumps({
        "commands" : [{"code":"switch_1","value":value}]
    }))
 	

her is the file to trigger the common file and Request class

Code: Select all

import common

value = common.set_state_mark_table(False)
print(value.text)
JamesG
Posts: 44

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

You can first take a look at this article: https://developer.tuya.com/en/docs/iot/ ... 0q34cs2e5g.

Check your program and if there are still issues, please provide feedback on the forum.

Tran Manh Duy
Posts: 10

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

I downloaded a Nodejs example code at:https://developer.tuya.com/en/docs/iot/ ... qza5d7iwkc
I successfully called GET method
But when I call POST method to send command control the device, it throws an error : Error: request api failed: sign invalid
This is source code:

Code: Select all

import * as qs from 'qs';
import * as crypto from 'crypto';
import { default as axios } from 'axios';

let token = '';

const config = {
  /* openapi host */
  host: 'https://openapi.tuyaus.com',
  /* fetch from openapi platform */
  accessKey: '',
  /* fetch from openapi platform */
  secretKey: '',
  /* Interface example device_ID */
  deviceId: '',
};

const httpClient = axios.create({
  baseURL: config.host,
  timeout: 5 * 1e3,
});

async function main() {
  await getToken();
  const data = await sendCommandControlDevice(config.deviceId);
  console.log('fetch success: ', JSON.stringify(data));
}

/**
 * fetch highway login token
 */
async function getToken() {
  const method = 'GET';
  const timestamp = Date.now().toString();
  const signUrl = '/v1.0/token?grant_type=1';
  const contentHash = crypto.createHash('sha256').update('').digest('hex');
  const stringToSign = [method, contentHash, '', signUrl].join('\n');
  const signStr = config.accessKey + timestamp + stringToSign;

  const headers = {
    t: timestamp,
    sign_method: 'HMAC-SHA256',
    client_id: config.accessKey,
    sign: await encryptStr(signStr, config.secretKey),
  };
  const { data: login } = await httpClient.get('/v1.0/token?grant_type=1', { headers });
  if (!login || !login.success) {
    throw Error(`fetch failed: ${login.msg}`);
  }
  token = login.result.access_token;
}

/**
 * GET method is success to fetch data
 */
async function getDeviceInfo(deviceId: string) {
  const query = {};
  const method = 'GET';
  const url = `/v1.0/devices/${deviceId}`;
  const reqHeaders: { [k: string]: string } = await getRequestSign(url, method, {}, query);

  const { data } = await httpClient.request({
    method,
    data: {},
    params: {},
    headers: reqHeaders,
    url: reqHeaders.path,
  });
  if (!data || !data.success) {
    throw Error(`request api failed: ${data.msg}`);
  }
}


/**
 * Fail when call POST method with Error: request api failed: sign invalid
 */
async function sendCommandControlDevice(deviceId: string) {
  const query = {};
  const method = 'POST';
  const url = `/v1.0/iot-03/devices/${deviceId}/commands`;
  const reqHeaders: { [k: string]: string } = await getRequestSign(url, method, {}, query);

  const { data } = await httpClient.request({
    method,
    data:  {
      "commands":[{"code":"switch_1","value":true}]
    },
    params: {},
    headers: reqHeaders,
    url: reqHeaders.path,
  });
  if (!data || !data.success) {
    throw Error(`request api failed: ${data.msg}`);
  }
}


/**
 * HMAC-SHA256 crypto function
 */
async function encryptStr(str: string, secret: string): Promise<string> {
  return crypto.createHmac('sha256', secret).update(str, 'utf8').digest('hex').toUpperCase();
}

/**
 * request sign, save headers 
 * @param path
 * @param method
 * @param headers
 * @param query
 * @param body
 */
async function getRequestSign(
  path: string,
  method: string,
  headers: { [k: string]: string } = {},
  query: { [k: string]: any } = {},
  body: { [k: string]: any } = {},
) {
  const t = Date.now().toString();
  const [uri, pathQuery] = path.split('?');
  const queryMerged = Object.assign(query, qs.parse(pathQuery));
  const sortedQuery: { [k: string]: string } = {};
  Object.keys(queryMerged)
    .sort()
    .forEach((i) => (sortedQuery[i] = query[i]));

  const querystring = decodeURIComponent(qs.stringify(sortedQuery));
  const url = querystring ? `${uri}?${querystring}` : uri;
  const contentHash = crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex');
  const stringToSign = [method, contentHash, '', url].join('\n');
  const signStr = config.accessKey + token + t + stringToSign;
  return {
    t,
    path: url,
    client_id: config.accessKey,
    sign: await encryptStr(signStr, config.secretKey),
    sign_method: 'HMAC-SHA256',
    access_token: token,
  };
}


main().catch(err => {
  throw Error(`error: ${err}`);
});


Please give me a solution or workaround to fix this issue to call POST method
Thanks

Last edited by Tran Manh Duy on 2024年 Jun 14日 18:36, edited 2 times in total.
panda-cat
Posts: 23

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

To facilitate request analysis, please send the most recent abnormal request result including tid, thank you.

Tran Manh Duy
Posts: 10

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

Here is console log. Pls refer to the attach file

Code: Select all

PS C:\Users\Downloads\nodejs_demo.ts (origin111)\nodejs_demo> npm run test

highway@1.0.0 test
ts-node nodejs_demo.ts

Error: error: Error: request api failed: sign invalid
    at C:\Users\Downloads\nodejs_demo.ts (origin111)\nodejs_demo\nodejs_demo.ts:146:9
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
PS C:\Users\Downloads\nodejs_demo.ts (origin111)\nodejs_demo>
Last edited by Tran Manh Duy on 2024年 Jun 14日 18:35, edited 1 time in total.
panda-cat
Posts: 23

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

Please refer to the modification of the arrow marked part:

async function sendCommandControlDevice(deviceId: string) {
const query = {};
--> const body = {"commands":[{"code":"switch_1","value":true}]};
const method = 'POST';
const url = /v1.0/iot-03/devices/${deviceId}/commands;
--> const reqHeaders: { [k: string]: string } = await getRequestSign(url, method, {}, query,body);

const { data } = await httpClient.request({
method,
--> data: body,
params: {},
headers: reqHeaders,
url: reqHeaders.path,
});
if (!data || !data.success) {
throw Error(request api failed: ${data.msg});
}
}

panda-cat
Posts: 23

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

To ensure the security of your account data, please protect your personal account key information. Please delete sensitive information in the question code or delete the old project and rebuild it.

Tran Manh Duy
Posts: 10

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

Thank you very much for supporting me. It worked. I can send the POST method success
And I deleted personal info in the above question

By the way, Could you give me the document or example to create GET, PUT, POST, and DELETE methods to call Tuya API?

panda-cat
Posts: 23

Re: Trouble with Tuya API POST request returning code 1004 or sign invalid

This Service SDKs should have the information you need: https://developer.tuya.com/en/docs/iot/ ... 09frvx48db

Post Reply