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.

velux kfl-200
#1
hello,
is there someone who already dealed with this interface with "hidden" ip api ?


I found someone who made a script:


Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
--[[ %% properties %% events %% globals --]] -- les variables globales ci-dessous sont initialisées avec des valeurs par défaut -- ces valeurs seront normalement écrasées par le passage de paramètres lors de l'appel de la scène. local VeluxDebug = true local programme = "2" local password = "xxxYYYzzz" local KLF_ip = "192.168.yyy.zzz" local params = fibaro:args() if (params) then     for k, v in ipairs(params) do        if (v.programme) then            programme = v.programme        end        if (v.adresseIP) then            KLF_ip = v.adresseIP        end        if (v.password) then             password = v.password          end     end end local urlLogin = 'http://'.. KLF_ip .. '/api/v1/auth' local urlLogout = 'http://'.. KLF_ip .. '/api/v1/auth' local urlScenes = 'http://'.. KLF_ip .. '/api/v1/scenes' local datasLogin = '{"action":"login","params":{"password":"'.. password .. '"}}' local datasLogout = '{"action":"logout","params":{}}' local datasAction = '{"action":"run","params":{"id":"' ..programme ..'"}}' if (VeluxDebug) then     fibaro:debug("Démarrage de la scène avec les paramètres suivants")     fibaro:debug("adresse IP = " .. KLF_ip)     fibaro:debug("Password = " .. password)     fibaro:debug("Programme = " .. programme)    fibaro:debug(datasAction) end local klf = net.HTTPClient() klf:request(urlLogin , {    success = function(response)         if tonumber(response.status)  == 200 then               if (VeluxDebug) then                 fibaro:debug("Call for LOGON successfull")                 fibaro:debug("Valeur de response")                 fibaro:debug(json.encode(response))            end                             -- récupération du token             local temp = json.encode(response)             local token = temp:match('\\\"token\\\":\\\"(.+)\\\",\\\"result')             token = string.gsub(token, '\\', '')               if (VeluxDebug) then                 fibaro:debug("Valeur du token : "..token)             end                             klf:request(urlScenes, {                       success = function(response)                           if tonumber(response.status) == 200 then                               if (VeluxDebug) then                             fibaro:debug("Call for ACTION successful")                             fibaro:debug("response\n")                             fibaro:debug(json.encode(response))                               end                        klf:request(urlLogout , {                            success = function(response)                                if tonumber (response.status) == 200 then                                    if (VeluxDebug) then                                         fibaro:debug("Call for LOGOUT successful")                                        fibaro:debug("response\n")                                        fibaro:debug(json.encode(response))                                        -- fibaro:debug("response.data\n")                                          -- fibaro:debug(response.data)                                      end                                  else                                        if (VeluxDebug)then                                         fibaro:debug("LOGOUT Failed : Status =" .. response.status)                                             end                                  end                            end,                            error = function(err)                                  print ("Erreur de LOGOUT")                                print('error = ' .. err)                            end,                            options = {                                method = 'POST',                                headers = {                                       ["content-type"] = 'application/json, charset=utf-8',                                       ["Authorization"] = "Bearer "..token,                                    ["Connection"] = 'close',                                   },                                data = datasLogout                            }                         })                     else                            if (VeluxDebug)then                             fibaro:debug("RUN ACTION Failed : Status =" .. response.status)                                 end                     end                end,                  error = function(err)                    print("Erreur lors du RUN ACTION")                    print('error =' ..err)                end,                  options = {                       method = 'POST',                       headers = {                           ["content-type"] = 'application/json, charset=utf-8',                        ["content-length"] = "34",                        ["Authorization"] = "Bearer "..token,                        ["Connection"] = 'Keep-Alive',                       },                    data = datasAction                }             })                                       else               if (VeluxDebug)then                 fibaro:debug("Login Failed : Status =" .. response.status)                     end           end                end,     error = function(err)         print ("Erreur lors du LOGIN")        print('error = ' .. err)     end,     options = {            method = 'POST',        headers = {             ["content-type"] = 'application/json, charset=utf-8',            ["connection"] = 'close',        },        data = datasLogin     } })
-----------
FRANCE SMARTHOME & SMARTBUILDING INTEGRATION
SE ECO EXPERT
Reply
#2
This code can be rewritten to run on LM, this thread has some similar examples of HTTP requests: https://forum.logicmachine.net/showthread.php?tid=993
Reply
#3
looks like Velux changed the API of the KLF200

https://velcdn.azureedge.net/~/media/com...er3-16.pdf

looks harder

an example


Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
## ====================================================== ## Script pour demarrer une scene de la box Velux KLF-200 ## ------------------------------------------------------ ## Version 0.1.1 - Last update : 03.11.18 by Christian R. ## ====================================================== import ssl, socket, time, struct from time import sleep # =============================================================================== # Variables # =============================================================================== SCENE_ID        = 1 KLF200_ADDRESS  = "192.168.0.12" PASSWORD        = "Velux123" PORT            = 51200 LoopDelay       = 1 # =============================================================================== # slip # =============================================================================== END     = b"\xC0"    # SLIP escape character as per RFC 1055 ESC     = b"\xDB"    # SLIP escape character as per RFC 1055 ESC_END = b"\xDC"    # SLIP escape character as per RFC 1055 ESC_ESC = b"\xDD"    # SLIP escape character as per RFC 1055 def slip_pack(inputFrame):        data = inputFrame    data = data.replace(ESC, ESC + ESC_ESC)    data = data.replace(END, ESC + ESC_END)    return END + data + END def slip_unpack(inputFrame):    data = inputFrame    if(data[0:1]==END and data[-1:]==END):        data = data.replace(ESC + ESC_END, END)        data = data.replace(ESC + ESC_ESC, ESC)        return data[1:-1]    else:        print("Error: No SLIP frame!\n")        return inputFrame # error -> return input # =============================================================================== # toolbox # =============================================================================== def getIndex(sourceDict, value):    return (k for k, v in sourceDict.items() if v == value).__next__() def toHex(s):    return ":".join("{:02x}".format(c) for c in s) # =============================================================================== # ActivateScene # =============================================================================== def process_connection(conn):    conn.settimeout(10.0) # 10 sec        print("Send valid password")    conn.write(bytes(ST_GW_PASSWORD_ENTER_REQ(PASSWORD)))    print("Received: ", toHex(slip_unpack(conn.recv())), "\n")        time.sleep(LoopDelay)    print("Activate Scene with ID = ", SCENE_ID)    conn.write(bytes(ST_GW_ACTIVATE_SCENE_REQ(bSceneID=SCENE_ID)))    print("Received: ", toHex(slip_unpack(conn.recv())))     def main():    sock = socket.socket(socket.AF_INET)    sock.settimeout(10.0)    context = ssl.SSLContext(ssl.PROTOCOL_TLS)    context.check_hostname = False    #accept self-signed certificate    context.verify_mode = ssl.CERT_NONE    conn = context.wrap_socket(sock, server_hostname=KLF200_ADDRESS)    try:        conn.connect((KLF200_ADDRESS, PORT))        process_connection(conn)    except BaseException as e:        raise(e)    finally:        conn.close() # =============================================================================== # klf200api # =============================================================================== GW_ACTIVATE_SCENE_REQ                       =  0x0412 GW_ACTIVATE_SCENE_CFM                       =  0x0413 GW_PASSWORD_ENTER_REQ                       =  0x3000 GW_PASSWORD_ENTER_CFM                       =  0x3001 dictPriorityLevel = {    0: 'Human Protection',    1: 'Environment Protection',    2: 'User Level 1',    3: 'User Level 2',    4: 'Comfort Level 1',    5: 'Comfort Level 2',    6: 'Comfort Level 3',    7: 'Comfort Level 4', } dictCommandOriginator = {    0x00: "LOCAL_USER",  # // User pressing button locally on actuator    0x01: "USER",  # // User Remote control causing action on actuator    0x02: "RAIN",  # // Sensor    0x03: "TIMER",  # // Sensor    0x04: "SECURITY",  # // SCD controlling actuator    0x05: "UPS",  # // UPS unit    0x06: "SFC",  # // Smart Function Controller    0x07: "LSC",  # // Lifestyle Scenario Controller    0x08: "SAAC",  # // Stand Alone Automatic Controls    0x09: "WIND",  # // Wind detection    0x10: "MYSELF",  # // Used when an actuator decides to move by itself    0xFE: "AUTOMATIC_CYCLE",  # // Used in context with automatic cycle;    0xFF: "EMERGENCY"  # // Used in context with emergency or security commands,    # // -this command originator should never be disabled } dictVelocity = {    0: 'DEFAULT',    1: 'SILENT',    2: 'FAST',    255: 'VELOCITY_NOT_AVAILABLE', #Only used in status reply } # ===============================================================================     class ST_GW_FRAME:    def __init__(self, Command):        self.DataLength    = 0        self.Command       = Command        self.binary_output = b"";    def __bytes__(self):        self.binary_output = struct.pack("BB", 0, self.DataLength + 3)        self.binary_output += struct.pack(">H", self.Command)        self.binary_output += self.pack_data()        self.binary_output += struct.pack("B", self.calc_crc())        return slip_pack(self.binary_output)        def calc_crc(self):        crc = 0        for sym in self.binary_output:            crc = crc ^ int(sym)        return crc    def pack_data(self):        return b"" class ST_GW_ACTIVATE_SCENE_REQ (ST_GW_FRAME):    def __init__(self,                 wSessionID         = 0x1234,                 CommandOriginator  = 'USER',                 PriorityLevel      = 'User Level 2',                 bSceneID           = 0,                 Velocity           = 'DEFAULT'):        ST_GW_FRAME.__init__(self, GW_ACTIVATE_SCENE_REQ)        self.DataLength         = 6        self.wSessionID         = wSessionID        self.bCommandOriginator = getIndex(dictCommandOriginator, CommandOriginator)        self.bPriorityLevel     = getIndex(dictPriorityLevel, PriorityLevel)        self.bSceneID           = bSceneID        self.bVelocity          = getIndex(dictVelocity, Velocity)            def pack_data(self):        ret = struct.pack(">H", self.wSessionID)        ret += bytes([self.bCommandOriginator])        ret += bytes([self.bPriorityLevel])        ret += bytes([self.bSceneID])        ret += bytes([self.bVelocity])        return ret class ST_GW_PASSWORD_ENTER_REQ (ST_GW_FRAME):    def __init__(self, Password):        ST_GW_FRAME.__init__(self, GW_PASSWORD_ENTER_REQ)        self.DataLength = 32        self.Password   = Password    def pack_data(self):        binary_data = bytes(self.Password,encoding='ascii')        binary_len = len(binary_data)        ret = binary_data[:self.DataLength if binary_len > self.DataLength else binary_len]        while binary_len < self.DataLength:            ret += b'\x00'            binary_len = binary_len + 1        return ret # =============================================================================== # Start script # ===============================================================================     main() print("Finished")
-----------
FRANCE SMARTHOME & SMARTBUILDING INTEGRATION
SE ECO EXPERT
Reply
#4
This is doable but quite hard to provide an working example without access to the device itself to test it.
Reply
#5
adminThis is doable but quite hard to provide an working example without access to the device itself to test it.

yes Sad 

can you provide at least exemple for the part with the initialisation:

GW_PASSWORD_ENTER_REQ( Last byte of Password byte array must be null terminated )

defaut password is
Code:
1
PASSWORD        = "Velux123"


because in fact i only need, the authentification, then the command send

GW_COMMAND_SEND_REQ()
-----------
FRANCE SMARTHOME & SMARTBUILDING INTEGRATION
SE ECO EXPERT
Reply
#6
Hi,

A full API made by Chris Traeger is available for Node.js. It is compatible with the latest versions of the KLF200 firmware

This would probably be a good base to implement something in LM. 

It is available at: https://github.com/PLCHome/velux-klf200-api

Cheers.
Michel.
Reply
#7
Here you can download the API documentation
The API password is the WLAN password of the KLF200
Reply
#8
(13.02.2019, 06:33)admin Wrote: This is doable but quite hard to provide an working example without access to the device itself to test it.

Yes - understandable.
The integration would be a big benefit for projects that include both KNX (LM) and Velux. But maybe Velux is not interested in helping with this integration now they have their own KNX-integration: DISK (Domex)
Reply
#9
Maybe using a Somfy Tahoma Switch with Velux IO products.

Somfy Tahoma Switch:
https://github.com/Somfy-Developer/Somfy...loper-Mode

Velux other post:
https://forum.logicmachine.net/showthrea...2#pid21082
Reply
#10
Even the “knx-lite” ABB-free@home have an integration:
ABB-free@home Velux Interface KLF 200

Somehow abb was able to get a Velux-developer-test-setup
Reply


Forum Jump: