I am having an hard time calling the Tuya API while using curl.
Tuya requires to generate a signature as following: HMAC-SHA256(client_id + t, secret). I built a small script that does exactly what Tuya asks. I have also double checked by trying to generate the signature using the same client_id, t and secret that are in their documentation as example, and the generated signature matches what the documentation says.
client_id is a pre-assigned value t is the timestamp in 13 digits (and here I think is where the error is) secret is a pre-assigned value
Once the signature is built it needs to be used via curl in a POST call, but Tuya keeps refusing the signature with the following error:
{"code":1004,"msg":"sign invalid","success":false,"t":1664314067553}
Now, I think that the issue is the timing. In order for my script to generate the signature few milliseconds are required and when the value of t gets passed to curl it won't match with the execution of curl (of course). Here's my code:
t=($(($(date +%s%N)/1000000))); sign1=$(echo -n "yyr8hxxxxxxxxd4mji$t" | openssl dgst -sha256 -hmac "cc75fd7xxxxxxxxx63d032b" | awk '{print$2}') && sign2=$(echo ${sign1^^}) ; curl --request POST "https://openapi.tuyaeu.com/v1.0/iot-03/devices/717715xxxxxxx520/commands" --header "sign_method: HMAC-SHA256" --header "client_id: yyr8hxxxxxxxxd4mji" --header "t: t" --header "mode: cors" --header "sign: $sign2" --header "access_token: cc75fd7xxxxxxxxx63d032b" --data "{"commands":[{"code":"switch_1","value":true}]}"
I've of course already tried to use && to execute all commands together but there has been no change. Does someone have any idea?
I had a use case where I wanted to pull the electric consumption of my devices from the Tuya smart plugs. I followed the Tuya API creation instruction from https://github.com/jasonacox/tuyapower and then encountered the same issue as you, getting {"code":1004,"msg":"sign invalid"," ...
forever.
The original answer from bobolecoco was not working for me either. Using Tuya's documentation on https://developer.tuya.com/en/docs/iot/singnature?id=Ka43a5mtx1gsc I figured out that the generated sign
was invalid due to line breaks, and used printf
instead of echo
. See below the code that's easy to debug and that works for me in bash 5.1.16.
# Set debug value to true or false to (de)activate output
debug=true
# Declare constants
ClientID="<<ENTER CLIENT ID HERE>>"
ClientSecret="<<ENTER CLIENT SECRET HERE>>"
BaseUrl="https://openapi.tuyaeu.com"
EmptyBodyEncoded="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
tuyatime=`(date +%s)`
tuyatime=$tuyatime"000"
if ($debug) then echo Tuyatime is now $tuyatime; fi;
# Get Access Token
URL="/v1.0/token?grant_type=1"
StringToSign="${ClientID}${tuyatime}GET\n${EmptyBodyEncoded}\n\n${URL}"
if ($debug) then echo StringToSign is now $StringToSign; fi;
AccessTokenSign=$(printf $StringToSign | openssl sha256 -hmac "$ClientSecret" | tr '[:lower:]' '[:upper:]' |sed "s/.* //g")
if ($debug) then echo AccessTokenSign is now $AccessTokenSign; fi;
AccessTokenResponse=$(curl -sSLkX GET "$BaseUrl$URL" -H "sign_method: HMAC-SHA256" -H "client_id: $ClientID" -H "t: $tuyatime" -H "mode: cors" -H "Content-Type: application/json" -H "sign: $AccessTokenSign")
if ($debug) then echo AccessTokenResponse is now $AccessTokenResponse; fi;
AccessToken=$(echo $AccessTokenResponse | sed "s/.*\"access_token\":\"//g" |sed "s/\".*//g")
if ($debug) then echo Access token is now $AccessToken; fi;
# Send Device status request
URL="/v1.0/iot-03/devices/status?device_ids=<<ENTER DEVICE IDs HERE, COMMA SEPARATED>>"
StringToSign="${ClientID}${AccessToken}${tuyatime}GET\n${EmptyBodyEncoded}\n\n${URL}"
if ($debug) then echo StringToSign is now $StringToSign; fi;
RequestSign=$(printf $StringToSign | openssl sha256 -hmac "$ClientSecret" | tr '[:lower:]' '[:upper:]' |sed "s/.* //g")
if ($debug) then echo RequestSign is now $RequestSign; fi;
RequestResponse=$(curl -sSLkX GET "$BaseUrl$URL" -H "sign_method: HMAC-SHA256" -H "client_id: $ClientID" -H "t: $tuyatime" -H "mode: cors" -H "Content-Type: application/json" -H "sign: $RequestSign" -H "access_token: $AccessToken")
if ($debug) then echo RequestResponse is now $RequestResponse; fi;
Your sign
is invalid.
Here are the steps you need (in bash) to call the Tuya API correctly.
Declare your variables:
ClientID="replace_with_you_client_Id yyr8hxxxxxxxxd4mji"
ClientSecret="replace_with_you_client_secret cc75fd7xxxxxxxxx63d032b"
Device="replace_with_your_device 717715xxxxxxx520"
First you have to get an access_token
:
AccessToken=$(t=$(date +%s%N |sed "s/......$//g"); curl -sSLkX GET "https://openapi.tuyaeu.com/v1.0/token?grant_type=1" -H "sign_method: HMAC-SHA256" -H "client_id: $ClientID" -H "t: $t" -H "mode: cors" -H "Content-Type: application/json" -H "sign: $(echo -en "${ClientID}${t}GET\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n\n/v1.0/token?grant_type=1" | openssl dgst -sha256 -hmac "$ClientSecret" | tr '[:lower:]' '[:upper:]' |sed "s/.* //g")" -H "access_token: " | sed "s/.*\"access_token\":\"//g" |sed "s/\".*//g")
Then you need to calculate the good "sign". You need you ClientId
, the timestamp, the access_token
, the method, the encoded_body
, the url
:
TimeStamp=$(date +%s%N |sed "s/......$//g")
METHOD='POST'
BODY='{"commands":[{"code":"switch_1","value":true}]}'
encodedBody=$(echo -n "$BODY" | openssl dgst -sha256 | sed "s/.*[ ]//g")
URL="/v1.0/iot-03/devices/$Device/commands"
Calculate sign
:
SIGN=$(echo -n "$ClientID${AccessToken}${TimeStamp}${METHOD}
$encodedBody
$URL" | openssl dgst -sha256 -hmac "$ClientSecret" | tr '[:lower:]' '[:upper:]' |sed "s/.* //g")`
Or in one line:
SIGN=$(echo -en "$ClientID${AccessToken}${TimeStamp}${METHOD}\n$encodedBody\n\n$URL" | openssl dgst -sha256 -hmac "$ClientSecret" | tr '[:lower:]' '[:upper:]' |sed "s/.* //g")
Then send the request:
curl -sSLkX $METHOD "https://openapi.tuyaeu.com$URL" -H "sign_method: HMAC-SHA256" -H "client_id: $ClientID" -H "t: $TimeStamp" -H "mode: cors" -H "Content-Type: application/json" -H "sign: $SIGN" -H "access_token: $AccessToken" -d "$BODY"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With