Here, for example, is the json for the Xantech in my v1. Works perfectly in my system. But I have been working on a rewrite that is more refined and powerful.
Note, it’s a commented jsonc file.
{
"matrix":{
"xantech_mx": {
"general":{
"description": "A generic template for a Xantech MX-88 Switcher",
"supported_models": ["MX-88ai"]
},
/*
Setup the basic configuration of the device
*/
"config":{
"interface": "tcp",
"port": 4999,
"prefix": "",
"suffix": "",
"delimiterOut": "", // Our commands include the + suffix, so no need to have one here
"delimiterIn": "+\r", // Received Responses are delimited by a + followed by \r
"substitutionCharacter": "$",
"statusResponseType": "regex", // This means our default processing engine for analysing responses is regex
"messageDelay": 250, // The device should have a rate throttle of 4 messages a second
"contentType": "text", // The payload of the messafe is handled as text
"messageFormat": "delimited", // Messages are delimited
"buffered": true, // We want to use buffer management to handle messages that don't come in, in one burst.
"contentTypeChoices": ["json", "html", "xml", "text", "byte"],
"messageFormatChoices": ["delimited", "stxetx", "byte", "fixed", "complete"],
"messageXform" : [{"from":"OK\r","to":""}, {"from":"ERROR\r","to":""}], // This device sends non-standard messages that we want to trap and throw away (hence the "")
"messageTimeout": 1000, // If we are awaiting a message, when should we timeout and move on (buffer management)
"messageLength": 0, // not used
"messageLengthByte": 0, // not used
"messageLengthIncrement": 0 // not used
},
/* commands: Define commands that can be sent to the device.
command: the command string sent to the device to do something
if its an http command, this is the GET/PUT/POST command
allows use of formula engine ${..}
statusRequest: is a corresponding Command that asks the device to report the status of the property.
allows use of formula engine ${..}
valueMap: is where we lookup the inbound human readable instruction (from a GUI or elsewhere) and translate it to the device code.
example: {"z1 power", "OFF"} -- we use the valueMap to translate OFF to 0. The ${%1} is the placeholder to insert that value.
if there is more than one value, we can use ${%1}, ${%2} etc..
if valueMap is missing, the system will look for a valueMap of the same name as the command.
actionSet: is for postprocessing.
"." is a special actionSet that says, after executing the command, execute a statusRequest. Typically used when the device doesn't automatically
update its status.
template: directs the system to use a template, for more complex command payloads or http body data
category: TODO
advanced: instructs the engine to execute advanced processing instructions
*/
"commands": {
"z1 power": {"command": "!1PR${%1}+", "statusRequest": "?1PR+", "valueMap":"power", "actionSet": "."},
"z2 power": {"command": "!2PR${%1}+", "statusRequest": "?2PR+", "valueMap":"power", "actionSet": "."},
"z3 power": {"command": "!3PR${%1}+", "statusRequest": "?3PR+", "valueMap":"power", "actionSet": "."},
"z4 power": {"command": "!4PR${%1}+", "statusRequest": "?4PR+", "valueMap":"power", "actionSet": "."},
"z5 power": {"command": "!5PR${%1}+", "statusRequest": "?5PR+", "valueMap":"power", "actionSet": "."},
"z6 power": {"command": "!6PR${%1}+", "statusRequest": "?6PR+", "valueMap":"power", "actionSet": "."},
"z7 power": {"command": "!7PR${%1}+", "statusRequest": "?7PR+", "valueMap":"power", "actionSet": "."},
"z8 power": {"command": "!8PR${%1}+", "statusRequest": "?8PR+", "valueMap":"power", "actionSet": "."},
"z1 input": {"command": "!1SS${%1}+", "statusRequest": "?1SS+", "valueMap":"input", "actionSet": "."},
"z2 input": {"command": "!2SS${%1}+", "statusRequest": "?2SS+", "valueMap":"input", "actionSet": "."},
"z3 input": {"command": "!3SS${%1}+", "statusRequest": "?3SS+", "valueMap":"input", "actionSet": "."},
"z4 input": {"command": "!4SS${%1}+", "statusRequest": "?4SS+", "valueMap":"input", "actionSet": "."},
"z5 input": {"command": "!5SS${%1}+", "statusRequest": "?5SS+", "valueMap":"input", "actionSet": "."},
"z6 input": {"command": "!6SS${%1}+", "statusRequest": "?6SS+", "valueMap":"input", "actionSet": "."},
"z7 input": {"command": "!7SS${%1}+", "statusRequest": "?7SS+", "valueMap":"input", "actionSet": "."},
"z8 input": {"command": "!8SS${%1}+", "statusRequest": "?8SS+", "valueMap":"input", "actionSet": "."},
"z1 mute": {"command": "!1MU${%1}+", "statusRequest": "?1MU+", "valueMap":"mute", "actionSet": "."},
"z2 mute": {"command": "!2MU${%1}+", "statusRequest": "?2MU+", "valueMap":"mute", "actionSet": "."},
"z3 mute": {"command": "!3MU${%1}+", "statusRequest": "?3MU+", "valueMap":"mute", "actionSet": "."},
"z4 mute": {"command": "!4MU${%1}+", "statusRequest": "?4MU+", "valueMap":"mute", "actionSet": "."},
"z5 mute": {"command": "!5MU${%1}+", "statusRequest": "?5MU+", "valueMap":"mute", "actionSet": "."},
"z6 mute": {"command": "!6MU${%1}+", "statusRequest": "?6MU+", "valueMap":"mute", "actionSet": "."},
"z7 mute": {"command": "!7MU${%1}+", "statusRequest": "?7MU+", "valueMap":"mute", "actionSet": "."},
"z8 mute": {"command": "!8MU${%1}+", "statusRequest": "?8MU+", "valueMap":"mute", "actionSet": "."},
"z1 volume": {"command": "!1VO${%1}+", "statusRequest": "?1VO+", "valueMap":"volume", "actionSet": "."},
"z2 volume": {"command": "!2VO${%1}+", "statusRequest": "?2VO+", "valueMap":"volume", "actionSet": "."},
"z3 volume": {"command": "!3VO${%1}+", "statusRequest": "?3VO+", "valueMap":"volume", "actionSet": "."},
"z4 volume": {"command": "!4VO${%1}+", "statusRequest": "?4VO+", "valueMap":"volume", "actionSet": "."},
"z5 volume": {"command": "!5VO${%1}+", "statusRequest": "?5VO+", "valueMap":"volume", "actionSet": "."},
"z6 volume": {"command": "!6VO${%1}+", "statusRequest": "?6VO+", "valueMap":"volume", "actionSet": "."},
"z7 volume": {"command": "!7VO${%1}+", "statusRequest": "?7VO+", "valueMap":"volume", "actionSet": "."},
"z8 volume": {"command": "!8VO${%1}+", "statusRequest": "?8VO+", "valueMap":"volume", "actionSet": "."},
"z1 volumedB": {"command": "!1VO${%1}+", "statusRequest": "?1VO+", "valueMap":"volumedB", "actionSet": "."},
"z2 volumedB": {"command": "!2VO${%1}+", "statusRequest": "?2VO+", "valueMap":"volumedB", "actionSet": "."},
"z3 volumedB": {"command": "!3VO${%1}+", "statusRequest": "?3VO+", "valueMap":"volumedB", "actionSet": "."},
"z4 volumedB": {"command": "!4VO${%1}+", "statusRequest": "?4VO+", "valueMap":"volumedB", "actionSet": "."},
"z5 volumedB": {"command": "!5VO${%1}+", "statusRequest": "?5VO+", "valueMap":"volumedB", "actionSet": "."},
"z6 volumedB": {"command": "!6VO${%1}+", "statusRequest": "?6VO+", "valueMap":"volumedB", "actionSet": "."},
"z7 volumedB": {"command": "!7VO${%1}+", "statusRequest": "?7VO+", "valueMap":"volumedB", "actionSet": "."},
"z8 volumedB": {"command": "!8VO${%1}+", "statusRequest": "?8VO+", "valueMap":"volumedB", "actionSet": "."},
"power": {"command": "!AO+", "statusRequest": "?1PR+"}
},
/* feedbacks: Define responses received from a device.
Order is important as the feedback array is processed from top to bottom.
Multiple matches are permitted
id: a unique id for each feedback.
statusReponse: the Regex or Object to match against the inbound payload. when it's an object, we need
a key (the target object). a value (the required matching value). valueKey = where to find the
value that we use in the valueMap lookup.
allows use of formula engine ${..}
{"key": "URI", "value":"/state/device/power_mode", "valueKey":"ITEMS.0.VALUE"}
category: the MQTT category or group that this feedback will show up in. default is "main"
topic: the actual descriptor of this feedback (defaults to id)
sph: instructs the engine to stop processing any more feedbacks.
valueMap: the valueMap to use to look up the human friendly display of the device property/feedback status.
requestReply: instructs the engine to require that the last "command" issued to the device, is a match, in
order for this feedback to be processed.
xform: tells the engine to convert the extracted value to a different dataType (i.e. a charcode becomes integer)
actionSet: tells the engine to send commands in a postProcessing actionSet.
advanced: instructs the engine to execute advanced processing instructions. overrides valueMap
*/
"feedbacks": [
{"id": "anyPower","statusResponse": "(?:^\\?)(\\d)PR(.{1})$", "category":"main", "advanced": true},
{"id": "power","statusResponse": "(?:^\\?\\dPR)(.{1})$", "category":"main", "valueMap": "systemPower"},
{"id": "z1 power","statusResponse": "(?:^\\?1PR)(.{1})$", "category":"zone 1", "sph":"end", "valueMap":"power"},
{"id": "z2 power","statusResponse": "(?:^\\?2PR)(.{1})$", "category":"zone 2", "sph":"end", "valueMap":"power"},
{"id": "z3 power","statusResponse": "(?:^\\?3PR)(.{1})$", "category":"zone 3", "sph":"end", "valueMap":"power"},
{"id": "z4 power","statusResponse": "(?:^\\?4PR)(.{1})$", "category":"zone 4", "sph":"end", "valueMap":"power"},
{"id": "z5 power","statusResponse": "(?:^\\?5PR)(.{1})$", "category":"zone 5", "sph":"end", "valueMap":"power"},
{"id": "z6 power","statusResponse": "(?:^\\?6PR)(.{1})$", "category":"zone 6", "sph":"end", "valueMap":"power"},
{"id": "z7 power","statusResponse": "(?:^\\?7PR)(.{1})$", "category":"zone 7", "sph":"end", "valueMap":"power"},
{"id": "z8 power","statusResponse": "(?:^\\?8PR)(.{1})$", "category":"zone 8", "sph":"end", "valueMap":"power"},
{"id": "z1 input","statusResponse": "(?:^\\?1SS)(.{1})$", "category":"zone 1", "sph":"end", "valueMap":"input"},
{"id": "z2 input","statusResponse": "(?:^\\?2SS)(.{1})$", "category":"zone 2", "sph":"end", "valueMap":"input"},
{"id": "z3 input","statusResponse": "(?:^\\?3SS)(.{1})$", "category":"zone 3", "sph":"end", "valueMap":"input"},
{"id": "z4 input","statusResponse": "(?:^\\?4SS)(.{1})$", "category":"zone 4", "sph":"end", "valueMap":"input"},
{"id": "z5 input","statusResponse": "(?:^\\?5SS)(.{1})$", "category":"zone 5", "sph":"end", "valueMap":"input"},
{"id": "z6 input","statusResponse": "(?:^\\?6SS)(.{1})$", "category":"zone 6", "sph":"end", "valueMap":"input"},
{"id": "z7 input","statusResponse": "(?:^\\?7SS)(.{1})$", "category":"zone 7", "sph":"end", "valueMap":"input"},
{"id": "z8 input","statusResponse": "(?:^\\?8SS)(.{1})$", "category":"zone 8", "sph":"end", "valueMap":"input"},
{"id": "z1 mute","statusResponse": "(?:^\\?1MU)(.{1})$", "category":"zone 1", "sph":"end", "valueMap":"mute"},
{"id": "z2 mute","statusResponse": "(?:^\\?2MU)(.{1})$", "category":"zone 2", "sph":"end", "valueMap":"mute"},
{"id": "z3 mute","statusResponse": "(?:^\\?3MU)(.{1})$", "category":"zone 3", "sph":"end", "valueMap":"mute"},
{"id": "z4 mute","statusResponse": "(?:^\\?4MU)(.{1})$", "category":"zone 4", "sph":"end", "valueMap":"mute"},
{"id": "z5 mute","statusResponse": "(?:^\\?5MU)(.{1})$", "category":"zone 5", "sph":"end", "valueMap":"mute"},
{"id": "z6 mute","statusResponse": "(?:^\\?6MU)(.{1})$", "category":"zone 6", "sph":"end", "valueMap":"mute"},
{"id": "z7 mute","statusResponse": "(?:^\\?7MU)(.{1})$", "category":"zone 7", "sph":"end", "valueMap":"mute"},
{"id": "z8 mute","statusResponse": "(?:^\\?8MU)(.{1})$", "category":"zone 8", "sph":"end", "valueMap":"mute"},
{"id": "z1 volume","statusResponse": "(?:^\\?1VO)(.*)$", "category":"zone 1", "valueMap":"volume", "sph":"end"},
{"id": "z2 volume","statusResponse": "(?:^\\?2VO)(.*)$", "category":"zone 2", "valueMap":"volume", "sph":"end"},
{"id": "z3 volume","statusResponse": "(?:^\\?3VO)(.*)$", "category":"zone 3", "valueMap":"volume", "sph":"end"},
{"id": "z4 volume","statusResponse": "(?:^\\?4VO)(.*)$", "category":"zone 4", "valueMap":"volume", "sph":"end"},
{"id": "z5 volume","statusResponse": "(?:^\\?5VO)(.*)$", "category":"zone 5", "valueMap":"volume", "sph":"end"},
{"id": "z6 volume","statusResponse": "(?:^\\?6VO)(.*)$", "category":"zone 6", "valueMap":"volume", "sph":"end"},
{"id": "z7 volume","statusResponse": "(?:^\\?7VO)(.*)$", "category":"zone 7", "valueMap":"volume", "sph":"end"},
{"id": "z8 volume","statusResponse": "(?:^\\?8VO)(.*)$", "category":"zone 8", "valueMap":"volume", "sph":"end"},
{"id": "z1 volumedB","statusResponse": "(?:^\\?1VO)(.*)$", "category":"zone 1", "valueMap":"volumedB", "sph":"end"},
{"id": "z2 volumedB","statusResponse": "(?:^\\?2VO)(.*)$", "category":"zone 2", "valueMap":"volumedB", "sph":"end"},
{"id": "z3 volumedB","statusResponse": "(?:^\\?3VO)(.*)$", "category":"zone 3", "valueMap":"volumedB", "sph":"end"},
{"id": "z4 volumedB","statusResponse": "(?:^\\?4VO)(.*)$", "category":"zone 4", "valueMap":"volumedB", "sph":"end"},
{"id": "z5 volumedB","statusResponse": "(?:^\\?5VO)(.*)$", "category":"zone 5", "valueMap":"volumedB", "sph":"end"},
{"id": "z6 volumedB","statusResponse": "(?:^\\?6VO)(.*)$", "category":"zone 6", "valueMap":"volumedB", "sph":"end"},
{"id": "z7 volumedB","statusResponse": "(?:^\\?7VO)(.*)$", "category":"zone 7", "valueMap":"volumedB", "sph":"end"},
{"id": "z8 volumedB","statusResponse": "(?:^\\?8VO)(.*)$", "category":"zone 8", "valueMap":"volumedB", "sph":"end"}
],
/* valueMaps: provide cross translation of device codes to human codes and vice versa.
deviceValue: the value required by (when inserted into a command), or reported by the device (when extracted from a feedback)
deviceValues can also be formulaic using the ${} syntax. (see volume valueMap below)
standardValue: one or more standardized values that will trigger a command attached to this map. i.e. "power", "OFF"
displayValue: the value to display to downstream GUI's or clients
displayValues can also be formulaic using the ${} syntax (see volume valueMap below)
feedbackActionSet: an actionSet to be be run, if this valueMap is a result of a feedback. This allows actionSets to be linked to specific values.
commandActionSet: an actionSet to be be run, if this valueMap is a result of a command. This allows actionSets to be linked to specific values.
clear: reset an entire mqtt node and its children. possibly useful on Power Off for some/many devices.
*/
"valuemaps":{
"power": [
{"deviceValue": "0","standardValue": [0,"OFF"], "displayValue": "OFF"},
{"deviceValue": "1","standardValue": [1,"ON"], "displayValue": "ON"}
],
"systemPower": [
{"deviceValue": "","standardValue":"", "displayValue": "${eval(['%z1p','%z2p','%z3p','%z4p','%z5p','%z6p','%z7p','%z8p'].indexOf('ON')>-1 ? 'ON':'OFF')}"}
],
"mute": [
{"deviceValue": "0","standardValue": [0,"OFF"], "displayValue": "OFF"},
{"deviceValue": "1","standardValue": [1,"ON"], "displayValue": "ON"}
],
"input": [
{"deviceValue": "1", "standardValue": [1, "INPUT1"], "displayValue": "INPUT 1"},
{"deviceValue": "2", "standardValue": [2, "INPUT2"], "displayValue": "INPUT 2"},
{"deviceValue": "3", "standardValue": [3, "INPUT3"], "displayValue": "INPUT 3"},
{"deviceValue": "4", "standardValue": [4, "INPUT4"], "displayValue": "INPUT 4"},
{"deviceValue": "5", "standardValue": [5, "INPUT5"], "displayValue": "INPUT 5"},
{"deviceValue": "6", "standardValue": [5, "INPUT6"], "displayValue": "INPUT 6"},
{"deviceValue": "7", "standardValue": [5, "INPUT7"], "displayValue": "INPUT 7"},
{"deviceValue": "8", "standardValue": [5, "INPUT8"], "displayValue": "INPUT 8"}
],
"volume": [
{"deviceValue": "${range(0,38,1,0,100)}", "standardValue": "",
"displayValue": "${range(0,100,1,0,38)}"}
],
"volumedB": [
{"deviceValue": "${eval((parseInt(%1)+38))}", "standardValue": "",
"displayValue": "${eval((parseInt((%1>18 ? (%1-38)*1.25 : %1>7 ? ((%1-25)*2.5)-7.5 : ((%1-52.5)*3.75)+118.125)*2)/2) + 'dB')}"}
]
},
"advanced":{
"updatePowerVariable":{
"feedback": "anyPower",
"type": "variable",
"inArray": false,
"variableMap":{
// all variables extracted in the variableMap are temporary.
"value" : "${%2}", // the feedback extracts two matches. one for matrix channel {%1} and one for power on or off {%2)
"name" : "z${%1}p",
"z${%1}p" : "${%2}" // we can do clver things like make the variable name a variable
},
"valueMap": "power",
"valueMapVariable": "z${%1}p", // when we call the valuemap, we need to specify which of the message value variables we
"outArray": false, // want to use. mostly it will be {%1}...but in this case its {%2}
"outArrayVariable": "",
"insertTemplate": { // the insertTemplate can use any variable from the variable map OR the deviceMap.variables
"parent" : "variables", // but it can't use the %1 etc.. as they don't exist in this scope
"node": "${%name}",
"template" : "${var(%name)}" // var is a special command. instead of "${var(%name)}", we could also have written ${%value}
}
}
},
"templates":{
// not used
},
"actionSets":{
"initialize":[
{"command": "z1 power", "value": "STATUS", "delay": "100"},
{"command": "z2 power", "value": "STATUS", "delay": "100"},
{"command": "z3 power", "value": "STATUS", "delay": "100"},
{"command": "z4 power", "value": "STATUS", "delay": "100"},
{"command": "z5 power", "value": "STATUS", "delay": "100"},
{"command": "z6 power", "value": "STATUS", "delay": "100"},
{"command": "z7 power", "value": "STATUS", "delay": "100"},
{"command": "z8 power", "value": "STATUS", "delay": "100"},
{"command": "z1 input", "value": "STATUS", "delay": "100"},
{"command": "z2 input", "value": "STATUS", "delay": "100"},
{"command": "z3 input", "value": "STATUS", "delay": "100"},
{"command": "z4 input", "value": "STATUS", "delay": "100"},
{"command": "z5 input", "value": "STATUS", "delay": "100"},
{"command": "z6 input", "value": "STATUS", "delay": "100"},
{"command": "z7 input", "value": "STATUS", "delay": "100"},
{"command": "z8 input", "value": "STATUS", "delay": "100"},
{"command": "z1 volume", "value": "STATUS", "delay": "100"},
{"command": "z2 volume", "value": "STATUS", "delay": "100"},
{"command": "z3 volume", "value": "STATUS", "delay": "100"},
{"command": "z4 volume", "value": "STATUS", "delay": "100"},
{"command": "z5 volume", "value": "STATUS", "delay": "100"},
{"command": "z6 volume", "value": "STATUS", "delay": "100"},
{"command": "z7 volume", "value": "STATUS", "delay": "100"},
{"command": "z8 volume", "value": "STATUS", "delay": "100"},
{"command": "z1 mute", "value": "STATUS", "delay": "100"},
{"command": "z2 mute", "value": "STATUS", "delay": "100"},
{"command": "z3 mute", "value": "STATUS", "delay": "100"},
{"command": "z4 mute", "value": "STATUS", "delay": "100"},
{"command": "z5 mute", "value": "STATUS", "delay": "100"},
{"command": "z6 mute", "value": "STATUS", "delay": "100"},
{"command": "z7 mute", "value": "STATUS", "delay": "100"},
{"command": "z8 mute", "value": "STATUS", "delay": "100"}
]
},
"variables":{
// not prefilled.
}
}
}
}