A codec can be used to apply transformations to incoming and outgoing data. Unlike apply functions, a codec is written in a separate JavaScript file which is referenced by the configuration.
To use a codec, configure the path to its JavaScript file using the codec
configuration setting. The codec will then be called to encode data before
publishing and to decode received data for all configured topics. The codec can decide which topics and properties to process, and can suppress messages
and generate additional messages as required.
A codec is a Node.js module which makes encode() and decode() functions available, which are called for
all properties or specific properties of the configured accessory. Codecs must implement a single function, init()
, exported through module.exports
. For example, here is a minimal codec implementation which does nothing:
function init() {
function encode( message ) {
return message; // no-op
}
function decode( message ) {
return message; // no-op
}
// return encode and decode functions
return {
encode,
decode
};
}
module.exports = {
init
};
This could also be written more concisely as:
module.exports = {
init: function() {
return {
encode( message ) {
return message;
},
decode( message ) {
return message;
}
}
}
}
A codec that is used by multiple accessories will only be loaded once, so any accessory-specific state must be stored within the init()
function. The choice to return encode()
and decode()
functions from init()
(as opposed to exporting them directly) is intended to make this easier.
The init()
function is passed a single object containing initialisation parameters for the accessory.
params.log
can be used to write to Homebridge's log.params.config
is the accessory's configuration (as configured inconfig.json
). This gives the codec access to the standard configuration settings, and lets it use its own if required.params.publish
may be used to publish to MQTT directlyparams.notify
may be used to send MQTT-Thing a property notification
The init()
function must return an object containing encode()
and decode()
functions (as described below). This can be just single encode()
and decode()
functions for all properties as above. More commonly a properties map containing property-specific functions is used, as follows:
function init() {
return {
properties: {
targetProp1: {
encode: encodeFunction1,
decode: decodeFunction1
},
targetProp2: {
encode: encodeFunction2
},
},
encode: defaultEncodeFunction,
decode: defaultDecodeFunction
}
}
The allows different encoding/decoding logic for each property. The default encode()
/decode()
functions are called for properties for which no property-specific function is defined.
The encode()
function is called to encode a message before publishing it to MQTT. It is passed three parameters:
message
is the message to be encodedinfo
is an object holding:info.topic
- the MQTT topic to be publishedinfo.property
- the property associated with the publishing operationinfo.extendedTopic
- the whole object passed in the configuration (null
if topic was a string)
output
is a function which may be called to deliver the encoded value asynchronously
The encode()
function may either return the encoded message, or it may deliver it asynchronously by passing it as a parameter to the provided output
function. It if does neither, no value will be published.
The decode
() function is called to decode a message received from MQTT before passing it for processing by MQTT-Thing. It takes three parameters:
message
is the message to be decodedinfo
is an object holding:info.topic
- the MQTT topic receivedinfo.property
the property associated with the received messageinfo.extendedTopic
- the whole object passed in the configuration (null
if topic was a string)
output
is a function which may be called to deliver the decoded value asynchronously
The decode()
function may either return the decoded message, or it may deliver it asynchronously by passing it as a parameter to the provided output
function. If it does neither, no notification will be passed on to MQTT-Thing.
The publish()
function provided in init()
's params
may be used to publish a message directly to MQTT.
topic
is the MQTT topic to publishmessage
is the message to publish to MQTT
The message is published directly to MQTT, ignoring any apply function usually with the topic and not passing through the Codec's encode()
function.
The notify()
function provided in init()
's params
may be used to notify MQTT-Thing of the new value for a property. This will deliver the notification to all internal subscribers to the property. Note that generally a corresponding MQTT 'get' topic must have been configured in order for internal subscribers to exist.
property
is the MQTT-Thing property to updatemessage
is the value to be passed to MQTT-Thing
The message is passed directly to MQTT-Thing. It does not pass through any apply function or through the Codec's decode()
function.
This section lists the properties available for each accessory type. All accessories may also support batteryLevel
, chargingState
and statusLowBattery
.
airPressure
, statusActive
, statusFault
, statusTampered
, statusLowBattery
airQuality
, statusActive
, statusFault
, statusTampered
, statusLowBattery
, carbonDioxideLevel
, pm10density
, pm2_5density
, ozonedensity
, nitrogenDioxideDensity
, sulphurDioxideDensity
, VOCDensity
, carbonMonoxideLevel
, airQualityPPM
, currentTemperature
, currentRelativeHumidity
carbonDioxideDetected
, carbonDioxideLevel
, carbonDioxidePeakLevel
, statusActive
, statusFault
, statusTampered
, statusLowBattery
contactSensorState
, statusActive
, statusFault
, statusTampered
, statusLowBattery
switch
, brightness
, volume
, motionDetected
on
, rotationDirection
, rotationSpeed
targetDoorState
, currentDoorState
, doorMoving
, obstructionDetected
, lockTargetState
, lockCurrentState
active
, currentHeaterCoolerState
, targetHeaterCoolerState
, currentTemperature
, lockPhysicalControls
, swingMode
, coolingThresholdTemperature
, heatingThresholdTemperature
, temperatureDisplayUnits
, rotationSpeed
, statusFault
currentRelativeHumidity
, statusActive
, statusFault
, statusTampered
, statusLowBattery
leakDetected
, statusActive
, statusFault
, statusTampered
, statusLowBattery
on
, brightness
, hue
, saturation
, colorTemperature
, white
, HSV
, RGB
, RGBW
, RGBWW
currentAmbientLightLevel
, statusActive
, statusFault
, statusTampered
, statusLowBattery
lockTargetState
, lockCurrentState
mute
, volume
motionDetected
, statusActive
, statusFault
, statusTampered
, statusLowBattery
occupancyDetected
, statusActive
, statusFault
, statusTampered
, statusLowBattery
on
, outletInUse
, currentConsumption
, voltage
, electricCurrent
, totalConsumption
targetState
, currentState
, statusFault
, statusTampered
mute
, volume
switch
, switch0
, switch1
, switch2
, ...
on
active
, input
XX
currentTemperature
, statusActive
, statusFault
, statusTampered
, statusLowBattery
currentHeatingCoolingState
, targetHeatingCoolingState
, currentTemperature
, targetTemperature
, temperatureDisplayUnits
, currentRelativeHumidity
, targetRelativeHumidity
, coolingThresholdTemperature
, heatingThresholdTemperature
, statusFault
active
, inUse
, setDuration
, remainingDuration
currentTemperature
, statusActive
, statusFault
, statusTampered
, statusLowBattery
, currentRelativeHumidity
, airPressure
, weatherCondition
, rain1h
, rain24h
, uvIndex
, visibility
, windDirection
, windSpeed
, maxwindSpeed
, Dewpoint
currentPosition
, targetPosition
, positionState
, holdPosition
, obstructionDetected
currentPosition
, targetPosition
, positionState
, holdPosition
, obstructionDetected
, targetHorizontalTiltAngle
, currentHorizontalTiltAngle
, targetVerticalTiltAngle
, currentVerticalTiltAngle
When writing a codec, you may find it helpful to start with the no-op implementation in test/empty-codec.js
.
Test examples of codec capabilities can be found in test/test-codec.js
.
This codec can be used to toggle the state of a switch whenever it receives any message.
{
"accessory": "mqttthing",
"type": "switch",
"name": "Toggle Switch",
"url": "mqtt-url",
"logMqtt": true,
"topics": {
"getOn": "test/toggle/get",
"setOn": "test/toggle/set"
},
"codec": "toggle.js"
}
/**
* Test 'toggle' codec - toggles switch on receipt of any message
* toggle.js
*/
'use strict'
module.exports = {
init: function() {
let state = false;
return {
properties: {
on: {
decode: function() {
state = ! state;
return state;
},
encode: function( msg ) {
state = msg;
return msg;
}
}
}
};
}
};
Codec state is declared within init(). The codec targets only the on property, toggling its state whenever it receives a message and recording the current state when publishing.
Codecs can be used to run arbitrary JavaScript code from within MQTT-Thing, not necessarily related to encoding/decoding messages. For example, the codec below sends a 'keep-alive' message at regular intervals. The message and send interval can be configured within the accessory configuration, in keepAliveMessage
and keepAliveInterval
respectively.
/**
* Test/Demo Homebridge-MQTTThing Codec (encoder/decoder)
* Codecs allow custom logic to be applied to accessories in mqttthing, rather like apply() functions,
* but in the convenience of a stand-alone JavaScript file.
*
* keep-alive-codec.js - sends keep-alive message at configurable interval
*/
'use strict';
module.exports = {
init: function( params ) {
let { config, publish } = params;
// publish keep-alive topic at regular interval
if( config.keepAliveTopic ) {
let keepAlivePeriod = config.keepAlivePeriod || 60;
let keepAliveMessage = config.keepAliveMessage || '';
setInterval( () => {
publish( config.keepAliveTopic, keepAliveMessage );
}, keepAlivePeriod * 1000 );
}
// no encode/decode in this codec
return {};
}
};
Built-in Codecs are provided with MQTT-Thing, and can be referenced without a path or .js
suffix. For example, to load the JSON Codec, use "codec": "json"
.
The JSON Codec aims to make JSON encoding and decoding, often implemented with apply() functions, easier to configure. If is intended for accessories which encode multiple properties as a JSON object sent over a single topic, instead of sending separate parameters in separate topics.
Mapping to and from JSON is configured using a jsonCodec object in the accessory configuration:
"jsonCodec": {
"properties": {
"on": "state.power",
"RGB": "state.rgb"
},
"fixed": { "fixed properties": "object (global/default)" },
"fixedByTopic": {
"topic1": { "fixed properties": "object for topic1",
"topic2": { "fixed properties": "object for topic2" }
},
"retain": true|false
}
The jsonCodec
configuration object should contain a properties
object containing strings indicating the JSON location of each property (as listed in Properties, above).
By default, the JSON codec only publishes properties which have updated. To collect all published properties for each published topic, set "retain": true
.
Fixed values (published with every message) may be specified in a fixed
object. If multiple topics are published which should have different fixed values, these may be specified through fixedByTopic
. Fixed values are not required in received messages; only mapped properties are extracted.
For example, the following accessory configuration:
{
"accessory": "mqttthing",
"type": "lightbulb",
"name": "Test RGB Light",
"url": "mqtt://192.168.10.35:1883",
"topics": {
"getRGB": "test/rgblight/get",
"setRGB": "test/rgblight/set",
"getOn": "test/rgblight/get",
"setOn": "test/rgblight/set"
},
"logMqtt": true,
"integerValue": false,
"codec": "json",
"jsonCodec": {
"properties": {
"on": "state.power",
"RGB": "state.rgb"
},
"fixed": {
"version": 1,
"sender": "MQTT-Thing"
}
}
}
... sends and receives messages like:
{"version":1,"sender":"MQTT-Thing","state":{"power":true,"rgb":"125,82,255"}}
The shellyAMAX Codec, created by Ferme de Pommerieux, allows the use of a Bosch AMAX alarm system with Shelly switches.
Example configuration:
{
"name": "AMAX",
"accessory": "mqttthing",
"url": "url",
"username": "user",
"password": "passwd",
"type": "securitySystem",
"codec": "shellyAMAX",
"ShellyGen": 1,
"AMAX": {
"setState": {
"Armed": {
"name": "shellies/shellyuni-98CDAC25XXXX",
"id": 0,
"ACTIVE": "on"
},
"Disarmed": {
"name": "shellies/shellyuni-98CDAC25XXXX",
"id": 0,
"ACTIVE": "on"
}
},
"getState": {
"Armed": {
"name": "shellies/shellyuni-98CDAC25XXXX",
"id": 0,
"ACTIVE": 1
},
"Triggered": {
"name": "shellies/shellyuni-98CDAC25XXXX",
"id": 1,
"ACTIVE": 0
},
"AltTriggered": {
"name": "shellies/shellyuni-98CDAC25XXXX",
"id": 1,
"ACTIVE": 1
}
}
},
"targetStateValues": [
"SA",
"AA",
"NA",
"D"
],
"currentStateValues": [
"SA",
"AA",
"NA",
"D",
"T"
],
"restrictTargetState": [
1,
3
],
"logMqtt": true
}