Hi all
With great thanks to everyone that has inspired / guided / assisted me in my development, I have built a couple of NodeRed flows that can be used with Velbus hardware.
(and are adaptable to other hardware)
Please note that I haven’t or can’t test these function nodes with all the Velbus modules, as I simply don’t have them all.
I’ll happily help you if you want to develop these function nodes further, but keep in mind that I am not a programmer, so any help is offered on a “best guess” basis.
The best thing to do is take a look at the protocol document for the Velbus module you want to work with and identify the correct packet contents that you are trying to send.
IE
- What is the command code?
- How many bytes should the packet have
- What are the parameters that you are trying to send?
VelbusLink has a great tool in the Logging window that helps you create packets and the checksum
Where I’ve already posted these concepts, I’ll simply add links.
Filtering a Velbus button event
Looping / Increasing / Decreasing a Volume with a Pressed or Long_Pressed event
[
{
"id": "f5370db.29be5f",
"type": "tab",
"label": "Media Control",
"disabled": false,
"info": ""
},
{
"id": "bfe462be.f71a5",
"type": "function",
"z": "f5370db.29be5f",
"name": "Dining Room Btn 3",
"func": "var event = msg.payload.event;\nvar channel = msg.payload.channel;\nvar newMsg = { payload: msg.payload.event };\nvar button = \"velbus:vmbgp2:c5053467:24:input#CH3\"\n\nif (channel === button ) {\n\tif (event === \"PRESSED\") {\n\t\treturn [msg, newMsg, null, null];\n\t}\n\tif (event === \"LONG_PRESSED\") {\n\t\treturn [msg, null, newMsg, null];\n\t}\n\tif (event === \"RELEASED\") {\n\t\treturn [msg, null, null, newMsg];\n\t}\n}\nreturn null;",
"outputs": 4,
"noerr": 0,
"x": 310,
"y": 140,
"wires": [
[],
[
"6ba85279.a48a9c"
],
[
"82c396c8.6ef1d8"
],
[
"f7ff8751.3c9c78"
]
],
"outputLabels": [
"Whole Msg",
"PRESSED",
"LONG_PRESSED",
"RELEASED"
]
},
{
"id": "6ba85279.a48a9c",
"type": "openhab2-get",
"z": "f5370db.29be5f",
"name": "",
"controller": "deaa963.282a968",
"itemname": "LoungeZone_Volume",
"x": 1080,
"y": 180,
"wires": [
[
"b93a65be.3a6588",
"804f9b5d.8649e8"
]
]
},
{
"id": "804f9b5d.8649e8",
"type": "function",
"z": "f5370db.29be5f",
"name": "Increase Volume value by 5",
"func": "var volume = Number(msg.payload.state);\n\nvar newVolume = 0;\n\n// newVolume = volume + 5;\nmsg.payload = volume;\n\n // global.set(\"LoungeDir\", 1);\n\nif (volume <= 90 ) {\n newVolume = volume + 5;\n msg.payload = newVolume;\n return [msg];\n\t}\n\n\n\nreturn [msg];",
"outputs": 1,
"noerr": 0,
"x": 1060,
"y": 300,
"wires": [
[
"c1079ec5.2dc68"
]
]
},
{
"id": "c1079ec5.2dc68",
"type": "debug",
"z": "f5370db.29be5f",
"name": "",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 1330,
"y": 280,
"wires": []
},
{
"id": "4954678d.cca448",
"type": "openhab2-out",
"z": "f5370db.29be5f",
"name": "Lounge Volume",
"controller": "deaa963.282a968",
"itemname": "LoungeZone_Volume",
"topic": "ItemCommand",
"payload": "",
"x": 1600,
"y": 380,
"wires": [
[]
]
},
{
"id": "82c396c8.6ef1d8",
"type": "looptimer",
"z": "f5370db.29be5f",
"duration": "1",
"units": "Second",
"maxloops": "15",
"maxtimeout": "1",
"maxtimeoutunits": "Minute",
"name": "",
"x": 1040,
"y": 80,
"wires": [
[
"6ba85279.a48a9c"
],
[]
]
},
{
"id": "f7ff8751.3c9c78",
"type": "change",
"z": "f5370db.29be5f",
"name": "Stop Loop",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "STOP",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 830,
"y": 40,
"wires": [
[
"82c396c8.6ef1d8"
]
]
},
{
"id": "876a61dd.69f63",
"type": "function",
"z": "f5370db.29be5f",
"name": "Dining Room Btn 4",
"func": "var event = msg.payload.event;\nvar channel = msg.payload.channel;\nvar newMsg = { payload: msg.payload.event };\nvar button = \"velbus:vmbgp2:c5053467:24:input#CH4\"\n\nif (channel === button ) {\n\tif (event === \"PRESSED\") {\n\t\treturn [msg, newMsg, null, null];\n\t}\n\tif (event === \"LONG_PRESSED\") {\n\t\treturn [msg, null, newMsg, null];\n\t}\n\tif (event === \"RELEASED\") {\n\t\treturn [msg, null, null, newMsg];\n\t}\n}\nreturn null;",
"outputs": 4,
"noerr": 0,
"x": 310,
"y": 700,
"wires": [
[],
[
"63273679.d5d1d8"
],
[
"e09c3db1.c1954"
],
[
"bf2758a3.db14c8"
]
],
"outputLabels": [
"Whole Msg",
"Pressed",
"Long_Pressed",
""
]
},
{
"id": "bf2758a3.db14c8",
"type": "change",
"z": "f5370db.29be5f",
"name": "Stop Loop",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "STOP",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 850,
"y": 680,
"wires": [
[
"e09c3db1.c1954"
]
]
},
{
"id": "e09c3db1.c1954",
"type": "looptimer",
"z": "f5370db.29be5f",
"duration": "1",
"units": "Second",
"maxloops": "15",
"maxtimeout": "1",
"maxtimeoutunits": "Minute",
"name": "",
"x": 1060,
"y": 600,
"wires": [
[
"63273679.d5d1d8"
],
[]
]
},
{
"id": "63273679.d5d1d8",
"type": "openhab2-get",
"z": "f5370db.29be5f",
"name": "",
"controller": "deaa963.282a968",
"itemname": "LoungeZone_Volume",
"x": 1080,
"y": 500,
"wires": [
[
"789bcbf6.c86824"
]
]
},
{
"id": "789bcbf6.c86824",
"type": "function",
"z": "f5370db.29be5f",
"name": "Decrease Volume value by 5",
"func": "var volume = Number(msg.payload.state);\n\nvar newVolume = 0;\n\n// newVolume = volume + 5;\nmsg.payload = volume;\n\n global.set(\"LoungeDir\", -1);\n\nif (volume >= 10 ) {\n newVolume = volume - 5;\n msg.payload = newVolume;\n return [msg];\n\t}\n\n\n\nreturn [msg];",
"outputs": 1,
"noerr": 0,
"x": 1060,
"y": 380,
"wires": [
[
"dd34678d.33fb18",
"4954678d.cca448"
]
]
},
{
"id": "dd34678d.33fb18",
"type": "debug",
"z": "f5370db.29be5f",
"name": "",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 1590,
"y": 500,
"wires": []
},
{
"id": "f873928f.82611",
"type": "link in",
"z": "f5370db.29be5f",
"name": "openHAB2 Events - Media Control",
"links": [
"86e41aa6.fc0848"
],
"x": 95,
"y": 140,
"wires": [
[
"bfe462be.f71a5",
"876a61dd.69f63"
]
]
},
{
"id": "b93a65be.3a6588",
"type": "function",
"z": "f5370db.29be5f",
"name": "Change Volume value by 5",
"func": "// Information from https://nodered.org/docs/user-guide/writing-functions\n\nvar volume = Number(msg.payload.state);\nvar button = String(msg.payload_in);\nvar newMsg = { payload:button};\nvar Dir = global.get(\"LoungeDir\");\n\n\nvar newVolume = 0;\n\n// newVolume = volume + 5;\nmsg.payload = volume;\n\nif (isNaN(Dir)) {\n global.set(\"LoungeDir\", 1);\n Dir = global.get(\"LoungeDir\");\n\n}\n\nif (volume <= 6 ) { if (Dir == -1) {\n newVolume = volume + 5;\n msg.payload = newVolume;\n global.set(\"LoungeDir\", 1);\n return [msg,newMsg];\n\t}}\n\t\nif (volume <= 90 ) { if (Dir == 1) {\n newVolume = volume + 5;\n msg.payload = newVolume;\n// global.set(\"LoungeDir\", 1);\n return [msg,newMsg];\n\t}}\n\t\nif (volume >= 90 ) { if (Dir == 1) {\n newVolume = volume - 5;\n msg.payload = newVolume;\n global.set(\"LoungeDir\", -1);\n return [msg,newMsg];\n\t}}\n\t\nif (volume <= 90 ) { if (Dir == -1) {\n newVolume = volume - 5;\n msg.payload = newVolume;\n// global.set(\"LoungeDir\", 1);\n return [msg,newMsg];\n\t}}\t\n\n\n\nreturn [msg,newMsg];",
"outputs": 2,
"noerr": 0,
"x": 1360,
"y": 120,
"wires": [
[
"deb42c17.7b645",
"4954678d.cca448"
],
[
"43fac0f6.8305b"
]
]
},
{
"id": "ec5643f1.2da01",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "PRESSED",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 340,
"y": 200,
"wires": [
[
"6ba85279.a48a9c",
"bf2758a3.db14c8",
"f7ff8751.3c9c78"
]
]
},
{
"id": "deb42c17.7b645",
"type": "debug",
"z": "f5370db.29be5f",
"name": "",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 1590,
"y": 220,
"wires": []
},
{
"id": "43fac0f6.8305b",
"type": "debug",
"z": "f5370db.29be5f",
"name": "",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 1590,
"y": 280,
"wires": []
},
{
"id": "569a9540.f364ec",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "LONG_PRESSED",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 310,
"y": 240,
"wires": [
[
"82c396c8.6ef1d8",
"bf2758a3.db14c8"
]
]
},
{
"id": "a2023990.27a8e8",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "10",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1370,
"y": 400,
"wires": [
[
"4954678d.cca448"
]
]
},
{
"id": "63c707ec.0969b8",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "RELEASED",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 330,
"y": 280,
"wires": [
[
"f7ff8751.3c9c78"
]
]
},
{
"id": "262ccd.b19f7334",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "PRESSED",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 336,
"y": 520,
"wires": [
[
"63273679.d5d1d8",
"f7ff8751.3c9c78",
"bf2758a3.db14c8"
]
]
},
{
"id": "1a38080c.e48678",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "LONG_PRESSED",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 306,
"y": 560,
"wires": [
[
"e09c3db1.c1954",
"f7ff8751.3c9c78"
]
]
},
{
"id": "6c6226ed.8bbc18",
"type": "inject",
"z": "f5370db.29be5f",
"name": "",
"topic": "",
"payload": "RELEASED",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 326,
"y": 600,
"wires": [
[
"bf2758a3.db14c8"
]
]
},
{
"id": "aec65e02.e71f2",
"type": "comment",
"z": "f5370db.29be5f",
"name": "Oscillating Volume control",
"info": "This function will oscillate the volume from 05 to 95 and back again",
"x": 1350,
"y": 60,
"wires": []
}
]
Set a Timer on a Relay
This uses a TCP node to directly inject values into VelServ
You’ll need to edit the Function node and just enter the base address and required relay channel
[
{
"id": "19e87fb3.6b71e",
"type": "tcp out",
"z": "1233381f.cbcdb8",
"host": "127.0.0.1",
"port": "6000",
"beserver": "client",
"base64": false,
"end": true,
"name": "Velserv OUT",
"x": 2650,
"y": 180,
"wires": []
},
{
"id": "29389d4b.a45f02",
"type": "function",
"z": "1233381f.cbcdb8",
"name": "Set channel of Velbus Relay",
"func": "var address = \"74\"; // Hex address of Relay\nvar channel = \"04\"; // Hex value of Channel No 3 of relay, derived from a bit array = 00100 (as in =bin2hex(00100) in a spreadsheet )\n\n\n\n\n\n// 01 = OFF, 02 = ON, 03 = Timer start\nvar eop = \"04\";\n\nvar a = '0f', b = 'f8', c = address, d = '02', e = '02', f = channel, g = '00', h =\"00\", i = \"00\", y = 'ff';\nvar ndigits = 2, x, carry = 0, z, checksum = \"\";\n\n\nvar incoming = String(msg.payload);\nvar buffer = \"\"\n\n\nvar newMsg = { payload:incoming };\n\n\nif (incoming == \"ON\" ) {\n \n command = \"02\"; // ON\n c = address;\n e = command;\n f = channel;\n \nfor (x = ndigits - 1; x >= 0; x--) {\n z = parseInt(a[x], 16) + parseInt(b[x], 16)+ parseInt(c[x], 16)+ parseInt(d[x], 16)+ parseInt(e[x], 16)+ parseInt(f[x], 16) + carry;\n carry = z >> 4;\n checksum = (z & 15).toString(16) + checksum;\n}\n checksum = ((parseInt(y, 16) - parseInt(checksum, 16))+1).toString(16)\n \n \n\nnewMsg.payload = checksum;\n\n \n \n \n \n \n// Format Pressed event\n// buffer = Buffer.from( [0x0F,0xF8,0x74,0x05,0x03,0x04,0x00,0x00,0x05,0x74,0x04] );\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}\n\n\nif (incoming == \"OFF\" ) {\n \n command = \"01\"; // OFF\n c = address;\n d = size;\n e = command;\n f = channel;\n \n\nfor (x = ndigits - 1; x >= 0; x--) {\n z = parseInt(a[x], 16) + parseInt(b[x], 16)+ parseInt(c[x], 16)+ parseInt(d[x], 16)+ parseInt(e[x], 16)+ parseInt(f[x], 16) + carry;\n carry = z >> 4;\n checksum = (z & 15).toString(16) + checksum;\n}\n checksum = ((parseInt(y, 16) - parseInt(checksum, 16))+1).toString(16)\n \n \n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}\n\t\n\t\n\t// Timer value less than 255 seconds\nif (incoming >= 0 ) { if (incoming <=255) {\n \n command = \"03\"; // Start Timer\n c = address;\n d = \"05\"; // Size\n e = command;\n f = channel;\n i = incoming.toString(16) // Duration of timer in seconds\n \n\n\n checksum = parseInt(a, 16) + parseInt(b, 16)+ parseInt(c, 16)+ parseInt(d, 16)+ parseInt(e, 16)+ parseInt(f, 16)+ parseInt(g, 16)+ parseInt(h, 16)+ parseInt(i, 16);\n checksum = checksum.toString(16);\n checksum = (parseInt(\"FFFF\",16) - parseInt(checksum,16));\n checksum = (parseInt(checksum,10)) + 257;\n checksum = checksum.toString(16);\n checksum = checksum.slice(-2);\n\n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+g.toString(16),\"0x\"+h.toString(16),\"0x\"+i.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}}\n\t\n\t\t// Timer value more than 255 seconds and less than 63535 seconds\nif (incoming >= 255 ) { if (incoming <=63535) {\n \n command = \"03\"; // Start Timer\n c = address;\n d = \"05\"; // Size\n e = command;\n f = channel;\n h = incoming - 255\n i = (incoming - h).toString(16) // Duration of timer in seconds\n \n\n\n checksum = parseInt(a, 16) + parseInt(b, 16)+ parseInt(c, 16)+ parseInt(d, 16)+ parseInt(e, 16)+ parseInt(f, 16)+ parseInt(g, 16)+ parseInt(h, 16)+ parseInt(i, 16);\n checksum = checksum.toString(16);\n checksum = (parseInt(\"FFFF\",16) - parseInt(checksum,16));\n checksum = (parseInt(checksum,10)) + 257;\n checksum = checksum.toString(16);\n checksum = checksum.slice(-2);\n\n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+g.toString(16),\"0x\"+h.toString(16),\"0x\"+i.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}}\n\t\n\tif (incoming >= 63535 ) {\n\t newMsg.payload =\"\";\n\t msg.payload =\"\";\n\t return;\n\t}\n\t\nreturn [msg,newMsg];",
"outputs": 2,
"noerr": 0,
"x": 2140,
"y": 180,
"wires": [
[
"1a157d79.4959a3",
"19e87fb3.6b71e"
],
[
"760977c6.8a7fd8"
]
],
"outputLabels": [
"Payload, Buffer value to TCP port",
"Checksum in HEX"
]
},
{
"id": "1a157d79.4959a3",
"type": "debug",
"z": "1233381f.cbcdb8",
"name": "Full TCP Buffer content",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 2410,
"y": 120,
"wires": []
},
{
"id": "760977c6.8a7fd8",
"type": "debug",
"z": "1233381f.cbcdb8",
"name": "Velbus packet Checksum",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 2410,
"y": 260,
"wires": []
},
{
"id": "8f1c0497.da7398",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "",
"topic": "",
"payload": "ON",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1810,
"y": 140,
"wires": [
[
"29389d4b.a45f02"
]
]
},
{
"id": "66c78a16.50a5e4",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "",
"topic": "",
"payload": "OFF",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1810,
"y": 180,
"wires": [
[
"29389d4b.a45f02"
]
]
},
{
"id": "c11edda3.df313",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "Timer value in seconds",
"topic": "",
"payload": "32",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1840,
"y": 220,
"wires": [
[
"29389d4b.a45f02"
]
]
}
]
Set a Dim level and a Fade time, per channel
This uses a TCP node to directly inject values into VelServ
The Velbus binding in openHAB2 allows for a global dim time to be set per module, so this is only for use where you want to have different dim times for each operation, or when using a VMB4DC and you want a different dim time per channel
[
{
"id": "ba1c35ab.79e078",
"type": "tcp out",
"z": "1233381f.cbcdb8",
"host": "192.168.178.42",
"port": "6000",
"beserver": "client",
"base64": false,
"end": true,
"name": "Velserv HOUSE OUT",
"x": 2600,
"y": 620,
"wires": []
},
{
"id": "9525a773.a53f38",
"type": "debug",
"z": "1233381f.cbcdb8",
"name": "Full TCP Buffer content",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 2330,
"y": 560,
"wires": []
},
{
"id": "9b9482a.e03158",
"type": "debug",
"z": "1233381f.cbcdb8",
"name": "Velbus packet Checksum",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 2330,
"y": 700,
"wires": []
},
{
"id": "a4d097f4.ee6ac8",
"type": "function",
"z": "1233381f.cbcdb8",
"name": "Set channel of Velbus Dimmer",
"func": "var address = \"03\"; // Hex address of Relay\nvar channel = \"01\"; // Hex value of Channel No 3 of relay, derived from a bit array = 00100 (as in =bin2hex(00100) in a spreadsheet )\nvar h = \"00\"; // High byte of dim speed\nvar i = \"05\"; // Low btye of dim speed\n\n\n\n\nvar eop = \"04\";\n\nvar a = '0f', b = 'f8', c = address, d = '02', e = '02', f = channel, g = '00', y = 'ff';\nvar checksum = \"\";\n\n\nvar incoming = String(msg.payload);\nvar buffer = \"\"\n\n\nvar newMsg = { payload:incoming };\n\n\nif (incoming == \"ON\" ) {\n \n incoming = 100; // Set Dim value to 100\n\t}\n\n\nif (incoming == \"OFF\" ) {\n \n incoming = 0; // Set Dim value to 0\n\t}\n\t\n\t\n\tif (incoming >= 0 ) { if (incoming <=101) {\n \n command = \"07\"; // Set Dim value\n d = \"05\"; // Size\n e = command;\n f = channel;\n g = incoming.toString(16); // Dim Value\n\n\n\n checksum = parseInt(a, 16) + parseInt(b, 16)+ parseInt(c, 16)+ parseInt(d, 16)+ parseInt(e, 16)+ parseInt(f, 16)+ parseInt(g, 16)+ parseInt(h, 16)+ parseInt(i, 16);\n checksum = checksum.toString(16);\n checksum = (parseInt(\"FFFF\",16) - parseInt(checksum,16));\n checksum = (parseInt(checksum,10)) + 257;\n checksum = checksum.toString(16);\n checksum = checksum.slice(-2);\n\n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+g.toString(16),\"0x\"+h.toString(16),\"0x\"+i.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}}\n\t\n\n\t\n\tif (incoming >= 100 ) {\n\t newMsg.payload =\"\";\n\t msg.payload =\"\";\n\t return;\n\t}\n\t\nreturn [msg,newMsg];",
"outputs": 2,
"noerr": 0,
"x": 2070,
"y": 620,
"wires": [
[
"9525a773.a53f38",
"ba1c35ab.79e078"
],
[
"9b9482a.e03158"
]
],
"outputLabels": [
"Payload, Buffer value to TCP port",
"Checksum in HEX"
]
},
{
"id": "d8808a88.5fd6a8",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "",
"topic": "",
"payload": "ON",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1730,
"y": 580,
"wires": [
[
"a4d097f4.ee6ac8"
]
]
},
{
"id": "7daca759.ac6df8",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "",
"topic": "",
"payload": "OFF",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1730,
"y": 620,
"wires": [
[
"a4d097f4.ee6ac8"
]
]
},
{
"id": "12295b6e.f81f05",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "Dim Value",
"topic": "",
"payload": "75",
"payloadType": "num",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1720,
"y": 660,
"wires": [
[
"a4d097f4.ee6ac8"
]
]
}
]
Change the active Program Group for an Input module
This uses a TCP node to directly inject values into VelServ
Where Velbus input modules can have 3 different timed events, changing the active group is fairly straightforward.
Inject a Number from 0 to 3
- 0 = None Active
- 1 = Group 1 (previously referred to as Winter)
- 2 = Group 2 (previously referred to as Summer)
- 3 = Group 3 (previously referred to as Holiday)
Just edit the function node and change the Base of the input module you want to address.
[
{
"id": "29389d4b.a45f02",
"type": "function",
"z": "1233381f.cbcdb8",
"name": "Set channel of Velbus Relay",
"func": "var address = \"74\"; // Hex address of Relay\nvar channel = \"04\"; // Hex value of Channel No 3 of relay, derived from a bit array = 00100 (as in =bin2hex(00100) in a spreadsheet )\n\n\n\n\n\n// 01 = OFF, 02 = ON, 03 = Timer start\nvar eop = \"04\";\n\nvar a = '0f', b = 'f8', c = address, d = '02', e = '02', f = channel, g = '00', h =\"00\", i = \"00\", y = 'ff';\nvar ndigits = 2, x, carry = 0, z, checksum = \"\";\n\n\nvar incoming = String(msg.payload);\nvar buffer = \"\"\n\n\nvar newMsg = { payload:incoming };\n\n\nif (incoming == \"ON\" ) {\n \n command = \"02\"; // ON\n c = address;\n e = command;\n f = channel;\n \nfor (x = ndigits - 1; x >= 0; x--) {\n z = parseInt(a[x], 16) + parseInt(b[x], 16)+ parseInt(c[x], 16)+ parseInt(d[x], 16)+ parseInt(e[x], 16)+ parseInt(f[x], 16) + carry;\n carry = z >> 4;\n checksum = (z & 15).toString(16) + checksum;\n}\n checksum = ((parseInt(y, 16) - parseInt(checksum, 16))+1).toString(16)\n \n \n\nnewMsg.payload = checksum;\n\n \n \n \n \n \n// Format Pressed event\n// buffer = Buffer.from( [0x0F,0xF8,0x74,0x05,0x03,0x04,0x00,0x00,0x05,0x74,0x04] );\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}\n\n\nif (incoming == \"OFF\" ) {\n \n command = \"01\"; // OFF\n c = address;\n d = size;\n e = command;\n f = channel;\n \n\nfor (x = ndigits - 1; x >= 0; x--) {\n z = parseInt(a[x], 16) + parseInt(b[x], 16)+ parseInt(c[x], 16)+ parseInt(d[x], 16)+ parseInt(e[x], 16)+ parseInt(f[x], 16) + carry;\n carry = z >> 4;\n checksum = (z & 15).toString(16) + checksum;\n}\n checksum = ((parseInt(y, 16) - parseInt(checksum, 16))+1).toString(16)\n \n \n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}\n\t\n\t\n\t// Timer value less than 255 seconds\nif (incoming >= 0 ) { if (incoming <=255) {\n \n command = \"03\"; // Start Timer\n c = address;\n d = \"05\"; // Size\n e = command;\n f = channel;\n i = incoming.toString(16) // Duration of timer in seconds\n \n\n\n checksum = parseInt(a, 16) + parseInt(b, 16)+ parseInt(c, 16)+ parseInt(d, 16)+ parseInt(e, 16)+ parseInt(f, 16)+ parseInt(g, 16)+ parseInt(h, 16)+ parseInt(i, 16);\n checksum = checksum.toString(16);\n checksum = (parseInt(\"FFFF\",16) - parseInt(checksum,16));\n checksum = (parseInt(checksum,10)) + 257;\n checksum = checksum.toString(16);\n checksum = checksum.slice(-2);\n\n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+g.toString(16),\"0x\"+h.toString(16),\"0x\"+i.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}}\n\t\n\t\t// Timer value more than 255 seconds and less than 63535 seconds\nif (incoming >= 255 ) { if (incoming <=63535) {\n \n command = \"03\"; // Start Timer\n c = address;\n d = \"05\"; // Size\n e = command;\n f = channel;\n h = incoming - 255\n i = (incoming - h).toString(16) // Duration of timer in seconds\n \n\n\n checksum = parseInt(a, 16) + parseInt(b, 16)+ parseInt(c, 16)+ parseInt(d, 16)+ parseInt(e, 16)+ parseInt(f, 16)+ parseInt(g, 16)+ parseInt(h, 16)+ parseInt(i, 16);\n checksum = checksum.toString(16);\n checksum = (parseInt(\"FFFF\",16) - parseInt(checksum,16));\n checksum = (parseInt(checksum,10)) + 257;\n checksum = checksum.toString(16);\n checksum = checksum.slice(-2);\n\n\n newMsg.payload = checksum;\n\n\n\nbuffer = Buffer.from([\"0x\"+a.toString(16),\"0x\"+b.toString(16),\"0x\"+c.toString(16),\"0x\"+d.toString(16),\"0x\"+e.toString(16),\"0x\"+f.toString(16),\"0x\"+g.toString(16),\"0x\"+h.toString(16),\"0x\"+i.toString(16),\"0x\"+checksum,0x04])\nreturn [{payload:buffer},newMsg];\n\n\n\t}}\n\t\n\tif (incoming >= 63535 ) {\n\t newMsg.payload =\"\";\n\t msg.payload =\"\";\n\t return;\n\t}\n\t\nreturn [msg,newMsg];",
"outputs": 2,
"noerr": 0,
"x": 2140,
"y": 180,
"wires": [
[
"1a157d79.4959a3",
"19e87fb3.6b71e"
],
[
"760977c6.8a7fd8"
]
],
"outputLabels": [
"Payload, Buffer value to TCP port",
"Checksum in HEX"
]
},
{
"id": "1a157d79.4959a3",
"type": "debug",
"z": "1233381f.cbcdb8",
"name": "Full TCP Buffer content",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 2410,
"y": 120,
"wires": []
},
{
"id": "19e87fb3.6b71e",
"type": "tcp out",
"z": "1233381f.cbcdb8",
"host": "127.0.0.1",
"port": "6000",
"beserver": "client",
"base64": false,
"end": true,
"name": "Velserv OUT",
"x": 2650,
"y": 180,
"wires": []
},
{
"id": "760977c6.8a7fd8",
"type": "debug",
"z": "1233381f.cbcdb8",
"name": "Velbus packet Checksum",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"x": 2410,
"y": 260,
"wires": []
},
{
"id": "8f1c0497.da7398",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "",
"topic": "",
"payload": "ON",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1810,
"y": 140,
"wires": [
[
"29389d4b.a45f02"
]
]
},
{
"id": "66c78a16.50a5e4",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "",
"topic": "",
"payload": "OFF",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1810,
"y": 180,
"wires": [
[
"29389d4b.a45f02"
]
]
},
{
"id": "c11edda3.df313",
"type": "inject",
"z": "1233381f.cbcdb8",
"name": "Timer value in seconds",
"topic": "",
"payload": "32",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"x": 1840,
"y": 220,
"wires": [
[
"29389d4b.a45f02"
]
]
}
]