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":""}]

Flow Info

Created 3 years, 6 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • delay (x1)
  • function (x5)
  • inject (x1)
  • mqtt in (x1)
  • mqtt out (x1)
  • mqtt-broker (x1)
  • switch (x6)
Other

Tags

  • smartweb
  • wirenboard
  • canbus
  • can-bus
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option