Hi everybody,
I’m presenting my solution. It works and serves the desired purpose. It can definitely be improved but it contains all required to build your own solution in OH3.
I’m open to any corrections or improvements. If someone knows a better and more performant HMAC library you’re highly welcome to advise.
Find my solution attached.
Control tuya devices via oh3v1.0.pdf (962.5 KB)
this.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getProperty("openhab.conf") : this.OPENHAB_CONF;
load(OPENHAB_CONF + "/libraries/rollups/hmac-sha256.js");
//load("/etc/openhab/libraries/rollups/hmac-sha256.js");
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
var newHashMap = Java.type("java.util.HashMap"); //Hashmap for header entries
var testProperty = Java.type("java.util.Properties"); //alternate way of defining header
var Mac = Java.type("javax.crypto.Mac");
var SecretKeySpec = Java.type ("javax.crypto.spec.SecretKeySpec");
var hashing = Java.type("java.util.Base64.Encoder");
var dateT = new Date(); // required for getting timestamp
var HttpUtil = Java.type("org.openhab.core.io.net.http.HttpUtil"); //required for executing HTTP call
var vclientID = "<client id>" //Client ID for API call
var secret = "<secret>"; //Secret for creating hash based message authentication code HMAC
var signMethod = "HMAC-SHA256"; //Sign Method for API call
var Url = "https://openapi.tuyaeu.com/v1.0/token?grant_type=1"; //Tuya API URL
var deviceID = "<device id>";
var UrlPost = "https://openapi.tuyaeu.com/v1.0/devices/"+deviceID+"/commands"
//var easyAccessToken = Java.type("org.openhab.core.transform.actions.Transformation"); //JSON Transofrmation HTTP response
//https://community.openhab.org/t/oh3-how-to-use-map-transform-in-ecma-rules/111908/7
//Get timestamp
var timeStamp=dateT.getTime(); //corret setup
//var timeStamp="1617225789665"; //testing
logger.info("Timestamp: "+timeStamp.toString());
//calc sign
var str = vclientID + timeStamp.toString(); //correct setup
//var str = "12345ucwaf51nm8qbvwu1617395205009";
logger.info("str: "+str);
/*var sha256_HMAC = Mac.getInstance("HmacSHA256");
logger.info("sha256_HMAC: "+sha256_HMAC.toString());
var secret_key = new SecretKeySpec(secret.getBytes("UTF-8"),"HmacSHA256");
logger.info("secret_key "+secret_key.toString());
sha256_HMAC.init(secret_key);
logger.info("sha256_HMAC: "+sha256_HMAC);
var hash = java.util.Base64.encoder.encodeToString(sha256_HMAC.doFinal(str.getBytes("UTF-8"))).toString();
var hash2 = sha256_HMAC.doFinal(str.getBytes("UTF-8")); */
var hash = CryptoJS.HmacSHA256(str, secret)
var hashInt64 = hash.toString();
hashInt64 = hashInt64.toUpperCase();
//var hash = hashing.encode(str.getBytes());
//hash.encode(str.getBytes());
logger.info("sign: "+hashInt64);
//logger.info("hash2 "+hash2);
//hash can be generate online for validation
//https://www.freeformatter.com/hmac-generator.html#ad-output
//https://codebeautify.org/hmac-generator?__cf_chl_captcha_tk__=f408d7ac52b42aaa5f151788489582ba85aa08c9-1617394342-0-AbRrY2T5r9t2goLd-DTR1_D8mJ2rGslNCyyC0SZ0g_5vdCyjUJo7gbwsIjZHtweoD5GRswehvl36PZvIb9EWgdNO01H9o-ZMzVCBxW7miKs8PYP2epnjk8O-EBjncARMkn_r9PZ3yp7TV12H9mDuhtFRmfTWP_sxC2ucBnanCUyMtahkwbTAwLNWpIVA1YcmiqL0mt63sTk_osgrwIDiYxV-VQHV8JChZEhRL5iqsSdBydu3885PymeFBP1iP6LdIIAtvy-ak0sqrcR77v-7jZY11u8SzfVVtNxVpI_RQlUIqzrd95cKnwITHnZ5ixXAgKTnouMLiEiJGYSz65Zfo6lwxaCfdAn8QT4onnyVAFM7nSrEqdLMuUgPoG58UsY3xHa9g6Xq3AyhPn1tDaegB9y3JOWHMcWilgg5QoNp2L5nPIvfTrwkZ1lDNgnulFdcbrZGBifVXtvFmMoIIU6juE0NEGJHNDY4r2x2BYRKz1flSHZ1efwwbmR0IUcMZZM3dntzVLcqJb-zLgriyD-LhJ_tyfAjMS6MX9E486WIWpUcL6vX-jsILmSDMH_eGETCacHUbEF6NQ90QK2sEwXjZ5IgKmDIVIyCWalneyBgyb9g
//Define Headers Option1 via Hashmap
//headers.put("client_id","xp49eucwaf51nm8qbvwu"); Real don't share
var headers = new newHashMap();
headers.put("client_id",vclientID);
headers.put("t",timeStamp.toString());
//headers.put("sign", signUp);
headers.put("sign_Method", signMethod);
headers.put("sign", hashInt64);
logger.info("headers: "+headers);
//Define Headers Option2 via Proterty
var headerTest = new testProperty();
headerTest.put("client_id",vclientID);
headerTest.put("t",timeStamp.toString());
//headerTest.put("sign", signUp);
headerTest.put("sign_Method", signMethod);
headerTest.put("sign", hashInt64);
logger.info("Headertest: "+headerTest);
//.Base64.encoder.encode(sha256_HMAC.init(secret_key));
//Base64.encodeBase64String(sha256_HMAC.doFinal(message.getBytes()));
//var hash = CryptoJS.HmacSHA256 (str,secret);
//var hash = new Mac.getInstance();
//var hashInBase64 = hash.toString();
//var signUp = hashInBase64.toUpperCase();
var httpResponse = HttpUtil.executeUrl("GET", Url, headerTest,null,null, 1000);
logger.info("HTTP response:"+httpResponse);
//example output:
// {"result":{"access_token":"f5197771b54c7350b6161b34bed8fdeb","expire_time":5302,"refresh_token":"da8ec9d910b027f73f7adc74020d6cab","uid":"bay16158430523737yAT"},"success":true,"t":1617478354451}
var tempTransform = JSON.parse(httpResponse);
//https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
var easyAccessToken = (tempTransform.result.access_token); //get correct element string
logger.info("accessToken: "+easyAccessToken);
//re-calculate Sign:
var timeStamp=dateT.getTime();
var str = vclientID+easyAccessToken+timeStamp.toString();
var hash = CryptoJS.HmacSHA256(str, secret)
var hashInt64 = hash.toString();
hashInt64 = hashInt64.toUpperCase();
//headers.put("access_token", easyAccessToken);
headerTest.put("access_token", easyAccessToken);
headerTest.put("t",timeStamp.toString());
//headerTest.put("sign", signUp);
headerTest.put("sign", hashInt64);
logger.info("Headertest: "+headerTest);
//executeUrl (String httpMethod, String url, Properties httpHeaders, InputStream content, String contentType, int timeout)
//value TRUE/FALSE defines switching light on or off
var contentPost = JSON.stringify({commands:[{code: 'switch_1', value: true}]});
logger.info("contentPost "+contentPost);
var input = org.apache.commons.io.IOUtils.toInputStream(contentPost, "UTF-8");
var httpPostResponse = HttpUtil.executeUrl("POST", UrlPost, headerTest,input,"application/JSON",2000);
logger.info("httpPostResponse: "+httpPostResponse);
//https://community.openhab.org/t/icloud-device-data-integration-in-openhab/32329
//x = new Date();
//h = x.getHours();
//logger.info("Current hour: Test Tuya"+h);
//var url = "https://openapi.tuyaeu.com/v1.0/token?grant_type=1";
//var response = sendHttpGetRequest(url);
//var Exec = Java.type("org.openhab.core.model.script.actions.Exec");
//var test = Exec.sendHttpGetRequest("https://google.com");
//logger.info("Test"+test);
//headers.put("Cache-control","no-cache");
//var output = Exec.sendHttpGetRequest("https://google.com");
//var output = test.sendHttpGetRequest("https://google.com");
//logger.info("HRRP sendHttpGetRequest"+output);
//var timeRightNow = new dtime();
//var output = HttpUtil.executeUrl("GET", "https://google.com", 2000) WORKS