This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Twinkly script
#1
I like to connect my Twinkly christmas leds to the LM.

I found a LUA example for Fibaro (see attached Twinkly.LUA), but i can't get the example working.

My code:
Code:
--[[ documentation api: https://xled-docs.readthedocs.io/en/latest/rest_api.html mqtt: https://xled-docs.readthedocs.io/en/latest/msqtt_api.html --]] -- init if not init then   require('socket.http')   require('ltn12')   require('json')   socket.http.TIMEOUT = 5   host = "192.168.x.xx"     authToken = nil   init = true end local api = {     base = "/xled/v1/",     endpoints = {         login = "login",         verify = "verify",         mode = "led/mode",         deviceName = "device_name",         reset = "led/reset",         movieConfig = "led/movie/config",         movie = "led/movie/full",         gestalt = "gestalt",         brightness = "led/out/brightness"     } } function getTwinklyResponseData(endPoint, body, content_type)     local content_type = content_type or "application/json"     local url = "http://" .. host .. api.base .. api.endpoints[endPoint]   local method = "GET"   if body ~= nil then     method = "POST"     if type(body) == "table" and content_type == "application/json" then       body = json.encode(body)     end   end   local response_body = {}   local payload = body   log('input', url, method, payload)   local res, code, response_headers, status = socket.http.request     {       url = url,       method = method,       headers =       {         ["Content-Type"] = content_type,         ["X-Auth-Token"] = authToken       },       source = ltn12.source.string(payload),       sink = ltn12.sink.table(response_body)     }   log(res, code, response_headers, status)   log('payload', payload)   log('response_body', response_body)   if res and code == 200 then     return true   else     return false   end end log('start') if getTwinklyResponseData("gestalt") then   getTwinklyResponseData("login",{challenge = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\a"}) end log('einde')

my data response from "gestalt" looks right with the correct device information, but the "login" gives as result:
Code:
* arg: 1   * number: 1 * arg: 2   * number: 200 * arg: 3   * table:    ["server"]     * string: esp-httpd/0.5    ["content-type"]     * string: application/json    ["connection"]     * string: close * arg: 4   * string: HTTP/1.1 200 OK
thats looks also right, but my response_data is: 
Code:
* arg: 1   * string: response_body * arg: 2   * table:    [1]     * string: {"code":1106}
and thats not right i think.

Who can help me?

Attached Files
.lua   Twinkly.LUA (Size: 10.11 KB / Downloads: 2)
Reply
#2
The challenge = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\a" part seems incorrect, try the one from the docs:
{challenge = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="}

You can also try MQTT instead of HTTP, the integration seems easier for MQTT.
Reply
#3
(07.12.2020, 10:02)admin Wrote: The challenge = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\a" part seems incorrect, try the one from the docs:
{challenge = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="}

same code 1106 response

-- i see this on https://labs.f-secure.com/blog/twinkly-t...ttle-star/
Once the application knows the IP address of the lights, it authenticates with them, receives an authentication token and retrieves information about the device.

The authentication process, although a good idea, is flawed. First, the application makes a POST request to the endpoint '/xled/v1/login' with a base64 encoded 32 bit random number. The lights respond with an authentication token, how long it will be valid for, and a base64 encoded response to the challenge. This response is based on the random challenge number, the MAC address of the lights and a shared secret. The phone application sets the authentication token as a HTTP header and sends the received challenge response back to the lights on the endpoint '/xled/v1/verify'. This finalises the authentication allowing for authenticated endpoints to be called.
Reply
#4
You are missing the Content-Length header for the POST request. This should help:
Code:
function getTwinklyResponseData(endPoint, body, content_type)   local content_type = content_type or "application/json"   local url = "http://" .. host .. api.base .. api.endpoints[endPoint]   local method = "GET"   local cl   if body ~= nil then     method = "POST"     if type(body) == "table" and content_type == "application/json" then       body = json.encode(body)     end     cl = #body   end   local response_body = {}   local res, code, response_headers, status = socket.http.request({     url = url,     method = method,     headers =     {       ["Content-Type"] = content_type,       ["Content-Length"] = cl,       ["X-Auth-Token"] = authToken     },     source = ltn12.source.string(body),     sink = ltn12.sink.table(response_body)   })   if res then     return code == 200   end end
Reply
#5
(07.12.2020, 11:14)admin Wrote: You are missing the Content-Length header for the POST request. This should help:
Code:
function getTwinklyResponseData(endPoint, body, content_type)   local content_type = content_type or "application/json"   local url = "http://" .. host .. api.base .. api.endpoints[endPoint]   local method = "GET"   local cl   if body ~= nil then     method = "POST"     if type(body) == "table" and content_type == "application/json" then       body = json.encode(body)     end     cl = #body   end   local response_body = {}   local res, code, response_headers, status = socket.http.request({     url = url,     method = method,     headers =     {       ["Content-Type"] = content_type,       ["Content-Length"] = cl,       ["X-Auth-Token"] = authToken     },     source = ltn12.source.string(body),     sink = ltn12.sink.table(response_body)   })   if res then     return code == 200   end end

yes, thanks
Reply
#6
Thanks admin for all the work. Here my first working version
Code:
--[[ documentation api: https://xled-docs.readthedocs.io/en/latest/rest_api.html mqtt: https://xled-docs.readthedocs.io/en/latest/msqtt_api.html fibaro example: https://forum.fibaro.com/topic/52767-qa-twinkly-lights-on-off/ --]] host = "xxx.xxx.x.xx"  -- enter your Twinkly ip -- debuggen debug = false function debugLog(iLog)   if debug then log(iLog) end end -- init if not init then   require('socket.http')   require('ltn12')   require('json')   socket.http.TIMEOUT = 5   challenceToken = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="   authToken = nil   init = true end local api = {     base = "/xled/v1/",     endpoints = {         login = "login",         verify = "verify",         mode = "led/mode",         deviceName = "device_name",         reset = "led/reset",         movieConfig = "led/movie/config",         movie = "led/movie/full",         gestalt = "gestalt",         brightness = "led/out/brightness"     } } function getTwinklyResponseData(endPoint, body, func, content_type)     debugLog(endPoint)       content_type = content_type or "application/json"     local url = "http://" .. host .. api.base .. api.endpoints[endPoint]     local method = "GET"       local cl       if body ~= nil then         method = "POST"         if type(body) == "table" and content_type == "application/json" then             body = json.encode(body)         end             cl = #body     end       debugLog({url, method, content_type, cl, authToken, body})       local response_body = {}     local res, code, response_headers, status = socket.http.request({       url = url,       method = method,       headers =       {         ["Content-Type"] = content_type,         ["Content-Length"] = cl,         ["X-Auth-Token"] = authToken       },       source = ltn12.source.string(body),       sink = ltn12.sink.table(response_body)     })       debugLog({res, code, response_headers, status, response_body})       if res then         if code == 200 then             func(json.decode(table.concat(response_body)))          else             func(nil, response_body)         end       else         func(nil, {error = code})       end end function connect(func_succ, func_err)   getTwinklyResponseData("gestalt", nil,   function(gestalt, err)       if err == nil then           getTwinklyResponseData(               "login",               {challenge = challenceToken},               function(login, err)                   if err == nil then                       authToken = login.authentication_token                       getTwinklyResponseData(                           "verify",                           {},                           function(verify, err)                               if err == nil then                                   func_succ(gestalt)                               else                                   if func_err then                                       func_err(err)                                   else                                       debugLog(json.encode(err))                                   end                               end                           end                       )                   else                       if func_err then                           func_err(err)                       else                           debugLog(json.encode(err))                       end                   end               end           )       else           if func_err then               func_err(err)           else               debugLog(json.encode(err))           end       end   end   ) end function setMode(mode, func)   if authToken == nil then       debugLog("Device is not intialized!")   end   getTwinklyResponseData(     "mode",     {mode = mode},     function(response, err)       if err == nil then           if func then             func(response)         end       else           debugLog(json.encode(err))       end     end   ) end function getMode(func)   if authToken == nil then       debugLog("Device is not intialized!")   end   getTwinklyResponseData(     "mode",     nil,     function(response, err)       if err == nil then         if func then             func(response)         end       else           debugLog(json.encode(err))       end     end   ) end function setBrightness(value, func)   if authToken == nil then       debugLog("Device is not intialized!")   end   getTwinklyResponseData(       "brightness",     {mode = "enabled", type = "A", value = value},     function(response, err)       if err == nil then         if func then             func(response)         end       else           debugLog(json.encode(err))       end     end   ) end function getBrightness(func)   if authToken == nil then       debugLog("Device is not intialized!")   end   getTwinklyResponseData(     "brightness",     nil,     function(response, err)       if err == nil then         if func then             func(response)         end       else           debugLog(json.encode(err))       end     end   ) end ------------------ some function tests ------------------ -- set mode connect(   function(data)     setMode(       "off", -- off, movie       function(data)         debugLog(json.encode(data))        end     )   end ) -- get current mode connect(   function(data)     getMode(       function(data)         debugLog(json.encode(data))        end       )   end ) -- set brightness connect(   function(data)     setBrightness(       10,       function(data)           debugLog(json.encode(data))       end     )   end ) -- get current brightness connect(   function(data)     getBrightness(       function(data)         debugLog(json.encode(data))        end     )   end )
Reply


Forum Jump: