Skip to content

Instantly share code, notes, and snippets.

@kaapyth0n
Last active January 29, 2021 21:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kaapyth0n/f6a5b7ffc8184a5a99c85b7b89f5cedb to your computer and use it in GitHub Desktop.
Save kaapyth0n/f6a5b7ffc8184a5a99c85b7b89f5cedb to your computer and use it in GitHub Desktop.
SmartWeb CAN-MQTT monitoring bridge
  • This flow polls CAN bus for SmartWeb programs.
  • Then parses all the incoming data and sends it to /devices/sw [PROGRAM_TYPE] [PROGRAM_ID]/controls/[PARAMETER_NAME] topic in formatted way
  • Also it pushes all the necessary .../meta tags, as described in https://github.com/wirenboard/homeui/blob/master/conventions.md
  • Also it watches the .../on topic for new values, converts it back to CAN format and sends it.

Examples:

/devices/sw ROOM_DEVICE 2/controls/roomT (current room temperature) /devices/sw ROOM_DEVICE 2/controls/roomComfortTemperature (room desired temperature in comfort mode)

Requires:

Limitations:

  • can only work with ROOM_DEVICE programs currently
[
{
"id": "856a283c.219248",
"type": "tab",
"label": "smartweb",
"disabled": false,
"info": ""
},
{
"id": "d2ebc1b8.e2fc1",
"type": "socketcan-out",
"z": "856a283c.219248",
"name": "socketcan-out",
"config": "b2855d5b.1215c8",
"x": 130,
"y": 180,
"wires": [
[
"a4485998.bd8828"
]
]
},
{
"id": "a4485998.bd8828",
"type": "switch",
"z": "856a283c.219248",
"name": "ext is true",
"property": "payload.ext",
"propertyType": "msg",
"rules": [
{
"t": "true"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 300,
"y": 180,
"wires": [
[
"b25d4f27.ef1b2"
]
]
},
{
"id": "b25d4f27.ef1b2",
"type": "switch",
"z": "856a283c.219248",
"name": "is response",
"property": "payload.canid",
"propertyType": "msg",
"rules": [
{
"t": "gte",
"v": "0x10000000",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 460,
"y": 180,
"wires": [
[
"711d767a.2d38b8"
]
]
},
{
"id": "711d767a.2d38b8",
"type": "function",
"z": "856a283c.219248",
"name": "parse msg",
"func": "msg.class =\"SNMsg\";\nmsg.programType = msg.payload.canid & 0xFF;\nmsg.programID = (msg.payload.canid & 0xFF00)>>8;\nmsg.functionID = (msg.payload.canid & 0xFF0000)>>16;\nmsg.messageFormat = (msg.payload.canid & 0x7000000)>>24;\nmsg.messageType = (msg.payload.canid & 0x18000000)>>27;\nmsg.body = msg.payload.data;\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "// Code added here will be run once\n// whenever the node is deployed.\nconst ROOM_DEVICE_JSON = `{\n\t\"class\": \"ROOM_DEVICE\",\n\t\"programType\": 5,\n\t\"functionList\": [],\n\t\"endClass\": true,\n\t\"implements\": [\"PROGRAM\"],\n\t\"inputs\": {\n\t\t\"roomT\":\t{\"id\":0, \"type\": \"temperature\"},\n\t\t\"rc21\": \t{\"id\":1, \"type\": \"rc21\"},\n\t\t\"floorT\": \t{\"id\":2, \"type\": \"temperature\"},\n\t\t\"wallT\": \t{\"id\":3, \"type\": \"temperature\"},\n\t\t\"humidity\":\t{\"id\":4, \"type\": \"humidity\"},\n\t\t\"CO2\": \t\t{\"id\":5, \"type\": \"onOff\"},\n\t\t\"presense\":\t{\"id\":6, \"type\": \"onOff\"}\n\t},\n\t\"outputs\": {\n\t\t\"floorValve\":\t{\"id\":0, \"type\": \"relay\"},\n\t\t\"radValve\":\t\t{\"id\":1, \"type\": \"relay\"},\n\t\t\"addValve\":\t\t{\"id\":2, \"type\": \"relay\"},\n\t\t\"floorSignal\":\t{\"id\":3, \"type\": \"PWM\"},\n\t\t\"radSignal\":\t{\"id\":4, \"type\": \"PWM\"},\n\t\t\"addSignal\":\t{\"id\":5, \"type\": \"PWM\"},\n\t\t\"ventilation\":\t{\"id\":6, \"type\": \"relay\"}\n\t},\n\t\"parameters\": {\n\t\t\"roomComfortTemperature\":\t{\"id\":1,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"roomReducedTemperature\":\t{\"id\":2,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"roomHysteresis\":\t\t\t{\"id\":3,\t\"encoding\":\"short10\",\t\"deprecated\":true,\t\"type\": \"temperature\"},\n\t\t\"relayPeriod\":\t\t\t\t{\"id\":4,\t\"encoding\":\"uint60K\",\t\"type\": \"minutes\"},\n\t\t\"responsibleCircuit1\":\t\t{\"id\":5,\t\"encoding\":\"ubyte\",\t\"type\": \"id\"},\n\t\t\"responsibleCircuit2\":\t\t{\"id\":6,\t\"encoding\":\"ubyte\",\t\"type\": \"id\"},\n\t\t\"responsibleCircuit3\":\t\t{\"id\":7,\t\"encoding\":\"ubyte\",\t\"type\": \"id\"},\n\t\t\"workMode\":\t\t\t\t\t{\"id\":8,\t\"encoding\":\"ubyte\", \"values\":{\"0\":\"Comfort\", \"1\":\"Economy\", \"2\":\"Schedule\", \"3\":\"Standby\"},\t\"type\": \"picklist\"},\n\t\t\"valveNormalState\":\t\t\t{\"id\":9,\t\"encoding\":\"ubyte\",\t\"type\": \"onOff\"},\n\t\t\"minimumFloorTemperature\":\t{\"id\":10,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"maximumFloorTemperature\":\t{\"id\":11,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"radiatorMinimumSignal\":\t{\"id\":12,\t\"encoding\":\"ubyte\",\t\"type\": \"%\"},\n\t\t\"roomDesiredTemperature\":\t{\"id\":13,\t\"encoding\":\"short10\", \"readOnly\":true,\t\"type\": \"temperature\"},\n\t\t\"relayPercentagePrimary\":\t{\"id\":14,\t\"encoding\":\"ubyte\", \"readOnly\":true,\t\"type\": \"%\"},\n\t\t\"relayPercentageSecondary\":\t{\"id\":15,\t\"encoding\":\"ubyte\", \"readOnly\":true,\t\"type\": \"%\"},\n\t\t\"outdoorTemperature\":\t\t{\"id\":16,\t\"encoding\":\"short10\", \"readOnly\":true,\t\"type\": \"temperature\"},\n\t\t\"relayPercentageAdditional\":\t{\"id\":17,\t\"encoding\":\"ubyte\", \"readOnly\":true,\t\"type\": \"%\"},\n\t\t\"circuit1Shift\":\t\t\t{\"id\":18,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"circuit2Shift\":\t\t\t{\"id\":19,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"circuit3Shift\":\t\t\t{\"id\":20,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"roomOffTemperature\":\t\t{\"id\":21,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"schedule\":\t\t\t\t\t{\"id\":22,\t\"encoding\":\"schedule1\"},\n\t\t\"floorRequiredTemperature\":\t{\"id\":23,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"currentFloorRequiredTemperature\":\t{\"id\":24,\t\"encoding\":\"short10\",\t\"type\": \"temperature\", \"readOnly\":true},\n\t\t\"wallRequiredTemperature\":\t{\"id\":25,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"currentWallRequiredTemperature\":\t{\"id\":26,\t\"encoding\":\"short10\",\t\"type\": \"temperature\", \"readOnly\":true},\n\t\t\"heatExtrication\":\t\t\t{\"id\":27,\t\"encoding\":\"ubyte\",\t\"type\": \"onOff\"},\n\t\t\"floorReducedTemperature\":\t{\"id\":28,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"wallReducedTemperature\":\t{\"id\":29,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"currentWorkModeStatus\":\t{\"id\":30,\t\"encoding\":\"ubyte\",\t\"type\": \"picklist\", \"values\":{\"0\":\"Comfort\", \"1\":\"Economy\", \"2\":\"ScheduleComfort\", \"3\":\"ScheduleEconomy\", \"4\":\"Standby\"}, \"readOnly\":true},\n\t\t\"ventilationCurcuit\":\t\t{\"id\":31,\t\"encoding\":\"ubyte\",\t\"type\": \"id\"},\n\t\t\"requiredHumidity\":\t\t\t{\"id\":32,\t\"encoding\":\"short10\",\t\"type\": \"humidity\"},\n\t\t\"poolCircuit\":\t\t\t\t{\"id\":33,\t\"encoding\":\"ubyte\",\t\"type\": \"id\"},\n\t\t\"poolTemperatureOffset\":\t{\"id\":34,\t\"encoding\":\"short10\",\t\"type\": \"temperature\"},\n\t\t\"schedule2\":\t\t\t\t{\"id\":35,\t\"encoding\":\"schedule2\"},\n\t\t\"location\":\t\t\t\t\t{\"id\":36,\t\"encoding\":\"ubyte\", \"type\": \"picklist\", \"values\": {\"24\": \"Room 5\", \"25\": \"Room 6\", \"26\": \"Room 7\", \"27\": \"Room 8\", \"20\": \"Room 1\", \"21\": \"Room 2\", \"22\": \"Room 3\", \"23\": \"Room 4\", \"28\": \"Room 9\", \"29\": \"Room 10\", \"1\": \"First floor\", \"0\": \"Ground floor\", \"3\": \"Hall\", \"2\": \"Attic\", \"5\": \"Dining room\", \"4\": \"Living room\", \"7\": \"Bathroom\", \"6\": \"Kitchen\", \"9\": \"Bedroom 2\", \"8\": \"Bedroom\", \"11\": \"Office\", \"10\": \"Bedroom 3\", \"13\": \"Nursery\", \"12\": \"Children's room\", \"15\": \"Corridor\", \"14\": \"Playroom\", \"17\": \"Shower\", \"16\": \"Bathroom 2\", \"19\": \"Office 2\", \"18\": \"Restroom\"}}\n\t}\n}`;\nlet PROGRAMS = flow.get(\"PROGRAMS\") || {};\nPROGRAMS[5] = JSON.parse(ROOM_DEVICE_JSON);\nflow.set(\"PROGRAMS\", PROGRAMS);\n\nlet C = {};\nC.PROGRAM = 1;\nC.SENSOR_N = 1;\nC.OUTPUT_N = 2;\nC.REMOTE_CONTROL = 22;\nC.GET_PARAMETER_VALUE = 1;\nC.SET_PARAMETER_VALUE = 2;\nflow.set(\"CONST\", C);",
"finalize": "",
"x": 630,
"y": 180,
"wires": [
[
"bc73bf98.92e26"
]
]
},
{
"id": "bc73bf98.92e26",
"type": "switch",
"z": "856a283c.219248",
"name": "programType",
"property": "programType",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "22",
"vt": "num"
},
{
"t": "eq",
"v": "1",
"vt": "num"
}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 310,
"y": 280,
"wires": [
[
"613f7859.b08cf8"
],
[
"28a5f358.769f5c"
]
]
},
{
"id": "613f7859.b08cf8",
"type": "switch",
"z": "856a283c.219248",
"name": "GET_PARAMETER_VALUE()",
"property": "functionID",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 610,
"y": 300,
"wires": [
[
"d39394f8.740708"
]
]
},
{
"id": "d39394f8.740708",
"type": "function",
"z": "856a283c.219248",
"name": "define the topic & value",
"func": "let result = [[msg]];\n\nfunction checkSet(topicc, value) {\n let topics = flow.get(\"topics\");\n if (topics === undefined) {\n topics = {};\n }\n let topicReplaced = topicc.replace(/ /g, '_');\n if (topics[topicReplaced] != value) {\n result[0].push({\n topic: topicc,\n payload: value,\n retain: true\n });\n topics[topicReplaced] = value;\n flow.set(\"topics\", topics);\n }\n}\n\nlet C = flow.get(\"CONST\");\nlet PROGRAMS = flow.get(\"PROGRAMS\");\nlet programs = flow.get(\"programs\")||{};\nif (programs[msg.programID] === undefined) {\n return null;\n}\nlet prgType = programs[msg.programID].programType;\nlet prg = PROGRAMS[prgType];\nif (prg === undefined) {\n return null;\n}\n//wb-adc/controls/5Vout\n\nlet deviceName = \"sw \" + prg.class + \" \" + msg.programID;\nlet topic = \"/devices/\" + deviceName + \"/controls/\";\nlet meta = {};\nlet iface = msg.body[0];\nlet parameterID = msg.body[1];\nlet ok = false;\nif (iface == C.PROGRAM) {\n if (parameterID == C.SENSOR_N) { //Sensor[N], N = 0, 1, 2…*\n let sensorID = msg.body[2];\n for (let sName in prg.inputs) {\n if (prg.inputs[sName].id == sensorID) {\n topic += sName;\n/*\nSENSOR_SHORT_VALUE -32768\nSENSOR_OPEN_VALUE -32767\nSENSOR_UNDEFINED -32766\n*/\n if (prg.inputs[sName].type == \"temperature\") {\n let buffer = new Uint8Array([msg.body[4], msg.body[3]]).buffer;\n let dataView = new DataView(buffer);\n value = dataView.getInt16(0);\n if (value == -32768 || value == -32767) {\n value = (value == -32768 ? \"КЗ\" : \"ХХ\");\n } else if (value == -32766) {\n break;\n } else {\n value = value/10;\n }\n meta.type = \"temperature\";\n meta.order = sensorID;\n meta.readonly = 1;\n ok = true;\n break;\n } else if (prg.inputs[sName].type == \"humidity\") {\n let buffer = new Uint8Array([msg.body[4], msg.body[3]]).buffer;\n let dataView = new DataView(buffer);\n value = dataView.getInt16(0);\n if (value == -32768 || value == -32767) {\n value = (value == -32768 ? \"КЗ\" : \"ХХ\");\n } else if (value == -32766) {\n break;\n } else {\n value = value/10;\n }\n meta.type = \"rel_humidity\";\n meta.order = sensorID;\n meta.readonly = 1;\n ok = true;\n break;\n } else if (prg.inputs[sName].type == \"onOff\") {\n let buffer = new Uint8Array([msg.body[4], msg.body[3]]).buffer;\n let dataView = new DataView(buffer);\n value = dataView.getInt16(0);\n if (value == -32768 || value == -32767) {\n value = (value == -32768 ? 1 : 0);\n } else if (value == -32766) {\n break;\n } else {\n value = value/10;\n }\n meta.type = \"switch\";\n meta.order = sensorID;\n meta.readonly = 1;\n ok = true;\n break;\n }\n }\n }\n } else if (parameterID == C.OUTPUT_N) { //Output[N], N = 0, 1, 2…*\n let outputID = msg.body[2];\n for (let oName in prg.outputs) {\n let output = prg.outputs[oName];\n if (output.id == outputID) {\n topic += oName;\n/*\nRELAIS_OFF = 0\nRELAIS_MIN = 1\nRELAIS_MAX = 254\nRELAIS_ON = 255\n*/\n if (output.type == \"relay\" || output.type == \"PWM\") {\n value = msg.body[3];\n if (output.type == \"relay\") {\n value = (value == 0 ? 0 : 1);\n meta.type = \"switch\";\n } else {\n value = value/254;\n if (value > 100) {\n value = 100;\n }\n meta.type = \"range\";\n checkSet(topic+\"/meta/max\", 100);\n checkSet(topic+\"/meta/units\", \"%\");\n }\n meta.order = outputID + 32;\n meta.readonly = 1;\n ok = true;\n break;\n }\n }\n }\n }\n} else if (PROGRAMS[iface] != undefined) { //program settings\n prg = PROGRAMS[iface];\n for (let pName in prg.parameters) {\n parameter = prg.parameters[pName];\n if (parameter.id == parameterID) {\n topic += pName;\n if (parameter.deprecated == true) {\n break;\n }\n if (parameter.encoding == \"short10\") {\n let buffer = new Uint8Array([msg.body[3], msg.body[2]]).buffer;\n let dataView = new DataView(buffer);\n value = dataView.getInt16(0);\n if (value == -32768 || value == -32767) {\n value = (value == -32768 ? \"КЗ\" : \"ХХ\");\n } else if (value == -32766) {\n break;\n } else {\n value = value/10;\n }\n if (parameter.type == \"temperature\") {\n meta.type = \"temperature\";\n } else if (parameter.type == \"humidity\") {\n meta.type = \"rel_humidity\";\n }\n meta.order = parameterID + 64;\n meta.readonly = (parameter.readOnly == true ? 1 : 0);\n ok = true;\n break;\n } else if (parameter.encoding == \"ubyte\") {\n value = msg.body[2];\n if (parameter.type == \"id\") {\n meta.type = \"text\";\n } else if (parameter.type == \"%\") {\n meta.type = \"range\";\n checkSet(topic+\"/meta/max\", 100);\n checkSet(topic+\"/meta/units\", \"%\");\n } else if (parameter.type == \"onOff\") {\n value = (value == 0 ? 0 : 1);\n meta.type = \"switch\";\n } else if (parameter.type == \"picklist\") {\n if (parameter.values == undefined || parameter.values[value] == undefined) {\n break;\n }\n value = parameter.values[value];\n meta.type = \"text\";\n }\n meta.order = parameterID + 64;\n meta.readonly = (parameter.readOnly == true ? 1 : 0);\n ok = true;\n break;\n } else if (parameter.encoding == \"uint60K\") {\n value = msg.body[2] + (msg.body[3]<<8) + (msg.body[4]<<16) + (msg.body[4]<<24);\n value = value/60000;\n meta.type = \"value\";\n meta.order = parameterID + 64;\n meta.readonly = (parameter.readOnly == true ? 1 : 0);\n checkSet(topic+\"/meta/units\", \"min\");\n ok = true;\n break;\n }\n }\n }\n}\nif (!ok) {\n return null;\n}\nmsg.topic = topic;\nmsg.payload = value;\nmsg.retain = true;\ncheckSet(\"/devices/\" + deviceName + \"/meta/name\", deviceName);\ncheckSet(topic+\"/meta/type\", meta.type);\ncheckSet(topic+\"/meta/order\", meta.order);\ncheckSet(topic+\"/meta/readonly\", meta.readonly);\nreturn result;\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 890,
"y": 300,
"wires": [
[
"516a190e.3cace8"
]
]
},
{
"id": "28a5f358.769f5c",
"type": "switch",
"z": "856a283c.219248",
"name": "I_AM_PROGRAM()",
"property": "functionID",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "7",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 570,
"y": 380,
"wires": [
[
"abedcc02.cb94f"
]
]
},
{
"id": "abedcc02.cb94f",
"type": "function",
"z": "856a283c.219248",
"name": "fill the network map",
"func": "let programs = flow.get('programs')||{};\nprograms[msg.programID]=\n {\n programID: msg.programID,\n controllerID: msg.body[1],\n programType: msg.body[2]\n };\nflow.set(\"programs\", programs);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 870,
"y": 380,
"wires": [
[]
]
},
{
"id": "516a190e.3cace8",
"type": "mqtt out",
"z": "856a283c.219248",
"name": "",
"topic": "",
"qos": "0",
"retain": "",
"broker": "e84856cd.dea338",
"x": 1110,
"y": 300,
"wires": []
},
{
"id": "84168715.8b3ef8",
"type": "inject",
"z": "856a283c.219248",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "true",
"payloadType": "bool",
"x": 110,
"y": 500,
"wires": [
[
"a7210d32.f98e1"
]
]
},
{
"id": "a7210d32.f98e1",
"type": "function",
"z": "856a283c.219248",
"name": "generate all request messages",
"func": "let result = [[]];\nlet PROGRAMS = flow.get(\"PROGRAMS\");\nlet programs = flow.get(\"programs\")||{};\nlet C = flow.get(\"CONST\");\n\ngetSNRqCANMessageID = function(classId, objectId, methodId) {\n\treturn (classId & 255) | ((objectId & 255) << 8) | ((methodId & 255) << 16);\n};\n\nfor (let program in programs) {\n let prgType = programs[program].programType;\n let prg = PROGRAMS[prgType];\n if (prg === undefined) {\n continue;\n }\n for (let input in prg.inputs) {\n input = prg.inputs[input];\n let msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, program, C.GET_PARAMETER_VALUE),\n dlc: 3,\n data: [C.PROGRAM, C.SENSOR_N, input.id]\n };\n result[0].push(msg);\n }\n for (let output in prg.outputs) {\n output = prg.outputs[output];\n let msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, program, C.GET_PARAMETER_VALUE),\n dlc: 3,\n data: [C.PROGRAM, C.OUTPUT_N, output.id]\n };\n result[0].push(msg);\n }\n for (let parameter in prg.parameters) {\n parameter = prg.parameters[parameter];\n let msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, program, C.GET_PARAMETER_VALUE),\n dlc: 2,\n data: [prgType, parameter.id]\n };\n result[0].push(msg);\n }\n for (let implemented_program_type in prg.implements) {\n for (let i_prg in PROGRAMS) {\n if (i_prg.class == implemented_program_type) {\n for (let parameter in i_prg.parameters) {\n let msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, program, C.GET_PARAMETER_VALUE),\n dlc: 2,\n data: [i_prg.programType, parameter.id]\n };\n result[0].push(msg);\n }\n break;\n }\n }\n }\n}\n\nresult[0].push({done: true});\n\nreturn result;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 370,
"y": 500,
"wires": [
[
"a59a1abb.4024a8"
]
]
},
{
"id": "a59a1abb.4024a8",
"type": "delay",
"z": "856a283c.219248",
"name": "",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "1",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": false,
"x": 640,
"y": 500,
"wires": [
[
"68f29126.8bb22"
]
]
},
{
"id": "56bf5d43.82a254",
"type": "socketcan-in",
"z": "856a283c.219248",
"name": "socketcan-in",
"config": "b2855d5b.1215c8",
"x": 1150,
"y": 500,
"wires": []
},
{
"id": "68f29126.8bb22",
"type": "switch",
"z": "856a283c.219248",
"name": "restart when done",
"property": "done",
"propertyType": "msg",
"rules": [
{
"t": "false"
},
{
"t": "true"
}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 890,
"y": 440,
"wires": [
[
"56bf5d43.82a254"
],
[
"a7210d32.f98e1"
]
]
},
{
"id": "cb8b7806.0612e8",
"type": "mqtt in",
"z": "856a283c.219248",
"name": "",
"topic": "/devices/+/controls/+/on",
"qos": "0",
"datatype": "auto",
"broker": "e84856cd.dea338",
"x": 160,
"y": 580,
"wires": [
[
"ad7b8493.b387d8"
]
]
},
{
"id": "ad7b8493.b387d8",
"type": "function",
"z": "856a283c.219248",
"name": "parse mqtt topic",
"func": "if (msg.topic.indexOf(\"/devices/sw \") !== 0) {\n return null;\n}\nlet a = \"/devices/sw \".length;\nlet b = msg.topic.indexOf(\" \", a) + 1;\nlet c = msg.topic.indexOf(\"/\", b);\nlet programID = msg.topic.substring(b, c);\nlet d = msg.topic.indexOf(\"/controls/\", c) + \"/controls/\".length;\nlet e = msg.topic.indexOf(\"/on\", d);\nlet paramName = msg.topic.substring(d, e);\n\nlet PROGRAMS = flow.get(\"PROGRAMS\");\nlet programs = flow.get(\"programs\");\nlet C = flow.get(\"CONST\");\n\ngetSNRqCANMessageID = function(classId, objectId, methodId) {\n\treturn (classId & 255) | ((objectId & 255) << 8) | ((methodId & 255) << 16);\n};\n\nlet program = programs[programID];\nif (program === undefined) {\n return null;\n}\n\nlet prgType = programs[programID].programType;\nlet prg = PROGRAMS[prgType];\nif (prg === undefined) {\n return null;\n}\nlet curPrg = prg;\n\nlet parameter = prg.parameters[paramName];\nif (parameter === undefined) {\n for (let iprgName in prg.implements) {\n for (let iprgType in PROGRAMS) {\n if (PROGRAMS[iprgType].class == iprgName) {\n parameter = PROGRAMS[iprgType].parameters[paramName];\n if (parameter === undefined) {\n continue;\n } else {\n curPrg = PROGRAMS[iprgType];\n }\n }\n }\n }\n}\nif (parameter === undefined) {\n return null;\n}\n\nif (parameter.encoding == \"short10\") {\n let value = msg.payload * 10;\n msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, programID, C.SET_PARAMETER_VALUE),\n dlc: 2,\n data: [curPrg.programType, parameter.id, value & 0xff, (value & 0xff00) >> 8]\n };\n} else if (parameter.encoding == \"ubyte\") {\n let value = msg.payload;\n if (parameter.type == \"picklist\") {\n let match = false;\n for (let option in parameter.values) {\n if (parameter.values[option] == value) {\n value = option;\n match = true;\n break;\n }\n }\n if (!match) {\n return null;\n }\n }\n msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, programID, C.SET_PARAMETER_VALUE),\n dlc: 2,\n data: [curPrg.programType, parameter.id, value]\n };\n} else if (parameter.encoding == \"uint60K\") {\n let value = msg.payload * 60000;\n msg = {};\n msg.payload = {\n ext: true,\n rtr: false,\n canid: getSNRqCANMessageID(C.REMOTE_CONTROL, programID, C.SET_PARAMETER_VALUE),\n dlc: 5,\n data: [\n curPrg.programType,\n parameter.id,\n value & 0xff,\n (value & 0xff00) >> 8,\n (value & 0xff0000) >> 16,\n (value & 0xff000000) >> 24]\n };\n}\n \nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 440,
"y": 580,
"wires": [
[
"56bf5d43.82a254"
]
]
},
{
"id": "b2855d5b.1215c8",
"type": "socketcan-config",
"interface": "can0"
},
{
"id": "e84856cd.dea338",
"type": "mqtt-broker",
"name": "",
"broker": "localhost",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": false,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willPayload": ""
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment