this is an Node for the API on the Velux© KLF-200 io-homecontrol© Gateway. (klf-200 provided by Velux©. I'm not affiliated.)
This implementation is based on the API documentation https://www.velux.com/api/klf200. It works only with the version 0.2.0.0.71.
Up to the version 0.1.1.0.45 it was possible to access the KLF-200 via the LAN interface. This does not work in version 0.2.0.0.71 anymore. Velux shared on demand with the Lan interface can only be addressed via the API.
Take a look at the technical specification for klf 200 api.pdf
For the latest updates see the CHANGELOG.md
npm install velux-klf200-api
GW_NODE_STATE_POSITION_CHANGED_NTF contains an incorrect timestamp. The lowest 2 bytes of the 4 bytes are sent to the higher 2 bytes and the lowest 2 bytes are 0. In response to the "GW_GET_ALL_NODES_INFORMATION_REQ" command, the correct time stamp is sent: 5be8d806 - 2018-11-12T01: 31: 50.000Z 15 sec Later a "GW_NODE_STATE_POSITION_CHANGED_NTF" hits. This contains a timestamp: d8160000 correct was here 5be8d816
If there is no communication with the KLF every 10 minutes to 15 minutes, the connection will be disconnected as described in the manual. If this happens when the home monitor "GW_HOUSE_STATUS_MONITOR_ENABLE_REQ" is activated, the KLF200 is no longer reachable. The KLF200 no longer sends the TLS command "Change Cipher Spec." on TLS start. This means that TLS encryption can no longer be initiated. I saw these in the wireshark. Should anyone else notice this error, it would be nice if that this someone also reports to the VELUX hotline.
The KLF200 needs more than 3 seconds to connect. It needs more then a second to send the key and more then a second for the TLS command "Change Cipher Spec.".
This API supported debugging. you have to set the environment variable DEBUG
set DEBUG=velux-klf200-api:*
The KLF-200 uses a self-signed certificate to secure the TLS protocol. This package contains the fingerprint and certificate that I have extracted from my KLF-200.
In case that your connection doesn't work due to a different certificate you have to extract the certificate from your box with the following command:
$ echo -n | openssl s_client -connect <ip adress of your KLF-200>:51200 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > velux-cert.pem
After extracting the certificate you have to generate the fingerprint with the following command:
$ openssl x509 -noout -fingerprint -sha1 -inform pem -in velux-cert.pem
This will print a fingerprint like 02:8C:23:A0:89:2B:62:98:C4:99:00:5B:D2:E7:2E:0A:70:3D:71:6A
.
The fingerprint is not checked by default. But you can check it if you set the fingerprint variable. You can exchange the certificate via the CA variable.
'use strict'
const velux = require('velux-klf200-api')
const fs = require('fs')
velux.fingerprint = '02:8C:23:A0:89:2B:62:98:C4:99:00:5B:D2:E7:2E:0A:70:3D:71:6A'
veluc.CA = fs.readFileSync('velux-cert.pem')
If the fingerprint is not set, it can be queried via velux.fingerprint
.
This API works with and without promise. You can use a callback function. If there is no callback function in the call the API create an promise object.
every API callback function is called first with an error object and then eventually a data object. If there is no error, the first parameter is null.
function callback(error,data) {}
'use strict'
const velux = require('velux-klf200-api')
step1()
function step1() {
velux.connect('10.10.10.15',{},step2)
}
function step2(err) {
if (err) {
step5(err)
} else {
velux.login('<some password>',step3)
}
}
function step3(err,data) {
if (err) {
step5(err)
} else {
velux.sendCommand({ api: velux.API.GW_GET_VERSION_REQ},step4)
}
}
function step4(err,data) {
if (err) {
step5(err)
} else {
console.log('step3:',data)
velux.sendCommand({ api: velux.API.GW_GET_PROTOCOL_VERSION_REQ},step5)
}
}
function step5(err,data) {
if (err) {
console.log(err)
} else {
console.log('step4:',data)
}
velux.end(step6)
}
function step6(err) {
if (err) {
console.log(err)
}
}
'use strict'
const velux = require('velux-klf200-api')
velux.connect('192.168.2.15',{})
.then(()=>{
return velux.login('<some password>')
})
.then((data)=>{
return velux.sendCommand({ api: velux.API.GW_GET_VERSION_REQ})
})
.then((data)=>{
console.log('step3:',data)
return velux.sendCommand({ api: velux.API.GW_GET_PROTOCOL_VERSION_REQ})
})
.then((data)=>{
console.log('step4:',data)
return velux.end()
})
.catch((err)=>{
console.log(err)
return velux.end()
})
in the API documentation I write the functions like that
(data)=>{}
it's the same as
function (data){}
step3: { id: 0,
api: 9,
apiText: 'GW_GET_VERSION_CFM',
softwareVersion: [ 0, 2, 0, 0, 71, 0 ],
hardwareVersion: 6,
productGroup: 14,
productType: 3 }
step4: { id: 0,
api: 11,
apiText: 'GW_GET_PROTOCOL_VERSION_CFM',
majorVersion: 3,
minorVersion: 14 }
connect (host, options ,cb)
login (password, cb)
sendCommand (data, cb)
end (cb)
on (eventName, listener)
off (eventName, listener)
once (eventName, listener)
The connect function establishes the connection to the KLF. According to VELUX documentation, only 2 connections are permitted for the KLF.
It returns an true in data if the API is connected.
this function executes the login. this must happen before calling another API.
this function corresponds to the API call:
sendCommand({ 'api': velux.API.GW_PASSWORD_ENTER_REQ,
'password': password})
This function sends a command to the KLF200 and waits for the answer The request and the answer are always objects. Only API commands that end with 'REQ' can be sent. The answer is always the appropriate confirm. This always ends with 'CFM'
The construction of the request object
{ api: velux.API.<command>_REQ,
<dataName>: <value>}
or
{ apiText: '<command>_REQ',
<dataName>: <value>}
The construction of the answer object
{ id: 0,
api: <commandNumber>,
apiText: '<command>_CFM',
<data>: <value>}
see examples and API below
The end function closes the connection to the KLF.
These are the functions for the event emitter. The KLF responds to each request with a confirm. Many data are also sent as a notification. These can only be retrieved via the events. For more information read this
When a notification arrives, the emitter is called once with 'NTF' and once with the API name. This allows you to react to each notification separately.
'use strict'
const velux = require('velux-klf200-api')
velux.on('NTF',(data)=>{
console.log(data)
})
velux.connect('192.168.2.15',{})
.then(()=>{
return velux.login('<some password>')
})
.then((data)=>{
return velux.sendCommand({ api: velux.API.GW_GET_ALL_NODES_INFORMATION_REQ })
})
.then((data)=>{
console.log(data)
})
.catch((err)=>{
console.log(err)
return velux.end()
})
'use strict'
const velux = require('velux-klf200-api')
velux.connect('192.168.2.15',{})
.then(()=>{
return velux.login('<some password>')
})
.then((data)=>{
return velux.sendCommand({ api: velux.API.GW_WINK_SEND_REQ,
commandOriginator: 1,
priorityLevel: 2,
winkStat: true,
winkTime: 10,
indexArrayCount: 1,
indexArray: [0]
})
})
.then((data)=>{
console.log(data)
})
.catch((err)=>{
console.log(err)
return velux.end()
})
'use strict'
const velux = require('velux-klf200-api')
velux.connect('192.168.2.15',{})
.then(()=>{
return velux.login('<some password>')
})
.then((data)=>{
return velux.sendCommand({ api: velux.API.GW_COMMAND_SEND_REQ,
commandOriginator: 1,
priorityLevel: 2,
parameterActive: 0,
functionalParameterMP: {valueType:'RELATIVE', value:100},
/* functionalParameterMP: 100, */
indexArrayCount: 1,
indexArray: [0],
priorityLevelLock: false,
lockTime: 0
})
})
.then((data)=>{
console.log(data)
})
.catch((err)=>{
console.log(err)
return velux.end()
})
The value is an Object, you can set the value to an explicit position or to an calculated position. when you set the value you can use either rawValue or value and valueType. It's also possible to use an number instead an Object. Then the API use Relative.
Access Method name for |Description |Range (Hex) |Size (Dec)
Standard Parameter | | |
valueType |value |rawValue |
RELATIVE |Relative value (0 – 100%) |0x0000–0xC800 |51201
RELATIVE |No feed-back value known |0xF7FF |1
PERCENT_PM |Percentage point plus or minus (-100% – 100%) |0xC900-0xD0D0 |2001
TARGET |The target value for the parameter |0xD100 |1
CURRENT |The current value for the parameter |0xD200 |1
DEFAULT |The default value for the parameter |0xD300 |1
IGNORE |Ignore the parameter field where this |0xD400 |1
|Access Method is written | |
Copyright (c) 2018 Chris Traeger
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.