Skip to content

Advanced Topic Notation

Martin Patfield edited this page Nov 10, 2025 · 9 revisions

JSONPath

For some devices, the desired values in the MQTT messages are embedded within a JSON object. For example, here is the MQTT message for my door lock that is received when the door is locked:

{ "time": 1750870005853, "state": 255 }

Since the value (255) that I need is embedded within JSON, I can use JSONPath syntax to tell the parser how to find the value.

So, in this example I would define my topic as:

zwave/1/door_lock/currentMode$.state

The $.state at the end tells the parser to grab the value using the key "state".

This can be arbitrarily complicated and several layers deep. For example,

{
  "time":1750870005853,
  "state": {
    "number": {
      "value": 255
    }
  }
}

would use the topic

zwave/1/door_lock/currentMode$.state.number.value

You can do the same for the set topic if the device is expecting a JSON object rather than a raw value.

If, for example, your device is expecting this message:

{
  "target": "away"
}

instead of just the raw value "away" you can use:

zwave/4/security/set$.target

Again, the $.target at the end tells the MQTT client to wrap the value a JSON object.

As with get topics, you can have an arbitrarily complex chain. So if, for example, you want this object:

{
  "target": {
    "mode": {
      "value": "away"
    }
  }
}

then you would use the topic

zwave/4/security/set$.target.mode.value

Value Transformers

There are other situations where the value needs to be adjusted or altered entirely.

For example, you may have an Air Quality Sensor that measures Nitrogen Dioxide in ppm (parts per million) instead of µg/m³ (micrograms per cubic meter) which HomeKit expects. In this case, you need to multiply by the arbitrary number 1883 to get the correct result.

Using the pipe (|) notation, you may add a transformer:

airquality/no2|value * 1883

This will multiply the value by 1883 to get the desired result.

Note that value is a reserved keyword here to indicate the incoming value you'd like to transform.

You can also do the same thing on the publish side to transfer it back to ppm. In this case, you would need to divide by 1883.

airquality/no2|value / 1883

If you need to save values for later use, you may use the special storage object which allows you to retain any arbitrary primitive value using "dot" notation, i.e. storage.foo='bar'

For example, here is a use case where a garage door needs to (sometimes) save the door position value:

stat/garagedoor/current|if(value=='C' || value=='O') storage.doorPosition=value; return value;

Then, the door position can be retrieved later with the following:

cmnd/garagerelay/POWER|return (storage.doorPosition===value)?undefined : 'ON'

Note that storage is shared across all topics for a particular MQTT instance, so take care to use unique keys.

Both JSONPath + Transformer

You can also combine JSONPath and Transformer notation into a single entity

airquality$.no2|value * 1883

Clone this wiki locally