formatBeforePublish behavior varies by channel type (MQTT binding)

Hi all,

I’ve been testing formatBeforePublish with different MQTT channel types and noticed that its behavior is highly type-dependent. The official docs say:

  • formatBeforePublish: Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say “MYCOLOR,”, you would use “MYCOLOR,%s”. Currently only “%s” is supported.

In practice, it works differently depending on the channel type.


Observed behavior of formatBeforePublish by channel type

Channel type %s works? %d works? Notes
Color :white_check_mark: :white_check_mark: Both work; numeric formatting is applied for %d.
Dimmer :white_check_mark: :cross_mark: Only %s works; %d is ignored, formatting not applied.
Color Temperature :white_check_mark: :cross_mark: Same as Dimmer; only %s works.
Rollershutter :cross_mark: :cross_mark: Neither %s nor %d works; raw value is published.
Number :cross_mark: :white_check_mark: Only %d works; numeric formatting applied.

Example things snippets

Color channel (works with %d or %s)

Type color : colorBulbC8CE39 "colorBulbC8CE39" [
    stateTopic="zigbee2mqtt/0x00178801102bbc90",
    transformationPattern="JSONPATH:$.color\",\"JSONPATH:$.brightness",
    commandTopic="zigbee2mqtt/0x00178801102bbc90/set",
    formatBeforePublish= "{\"color\": {\"hue\": %d, \"saturation\": %d}, \"brightness\":%d}",
    colorMode="HSB"
]

Dimmer channel (only works with %s)

Type dimmer : dimmerBulbC642E4 "dimmerBulbC642E4" [
    stateTopic="zigbee2mqtt/0x00178801011fa261",
    transformationPattern="JSONPATH:$.brightness∩JS:zigbee2mqtt_brightness_in.js",
    commandTopic="zigbee2mqtt/0x00178801011fa261/set",
    formatBeforePublish= "{\"brightness\": \"%s\"}",
    transformationPatternOut="JS:zigbee2mqtt_brightness_out.js",
    min=0,
    max=100,
    step=1,
    unit="%"
]

Color Temperature channel (only works with %s)

Type colorTemperature : ctBulbC8CE39 "Color Temp" [
    stateTopic="zigbee2mqtt/0x0017880110abcd/set",
    commandTopic="zigbee2mqtt/0x0017880110abcd/set",
    formatBeforePublish= "{\"color_temp\": \"%s\"}",
    min=153,
    max=500
]

Rollershutter channel (does not support formatting)

Type rollershutter : rollerBulb1234 "Roller" [
    stateTopic="zigbee2mqtt/0x0017880110efgh",
    commandTopic="zigbee2mqtt/0x0017880110efgh/set"
]

Number channel (only works with %d)

Type number : numberBulb5678 "Number Example" [
    stateTopic="zigbee2mqtt/0x00178801101234",
    commandTopic="zigbee2mqtt/0x00178801101234/set",
    formatBeforePublish="%d",
    min=0,
    max=100
]

Key takeaways

  1. Type-sensitive behavior: formatBeforePublish behaves differently depending on the channel type.
  2. Dimmer & Color Temperature channels only accept %s.
  3. Rollershutter does not support formatting.
  4. Number channels only work with %d.
  5. The documentation (“only %s is supported”) is not fully accurate; behavior varies by channel type.

Question for the community

Is this type-specific behavior intentional?
It seems that numeric formatting (%d) is selectively supported depending on the channel type.

I hope this helps clarify formatBeforePublish usage for others — any additional insights or confirmations are appreciated!

Given that it explicitly says only %s is supported I’m surprised and %d works. I suspect it only works by accident.

What gets sent? A color is represented by three numbers and there’s nothing here to tell it what number of the three to “format”?

This may not throw an error but she’s it actually work? All things considered if expect it to just list the same number three times, either the hue or the brightness (since a HSBState can be used like a Dimmer.

As for the others, all I can say is given different types the formatting is necessarily going to work differently. It makes no sense to use %d on a String Channel, for example. Color is made up of three values, not just one. Over all, I would expect the different channels to behave differently. Whether that’s intentional or not I can’t say.

The usage for the formatting is to add stuff to the state. Did you try something like "Number " %s for the number Channel?

Did you do any tests with %f? That’s going to be more useful for Number Channels than %d anyway.

It sends
{color:hue:xxx,saturation:xx,brightness:xxx}

But is “xxx” the same number for each? And if so, which number is it, the hue or the brightness?

A Color Item is made up of three numbers and there is nothing here to tell the formatter which of the three numbers you are trying to format.

No different numbers and they are according to the values of the item…

OK, then ColorChannel most definitely has some special processing applied. If you tried to do that anywhere else (e.g. in the label or state description for an Item) it wouldn’t work.

Item 'Color_GF_Corridor_ExtendedLight1' changed from 35.462,74.42800,100 to 35,17,100

in mqtt

{"color": {"hue": 35, "saturation": 17}, "brightness":100}

Sorry to reopen this but it’s doing my head in. I have some Zigbee roller blinds. To set an intermediate position I need to send {“position”: x} where x is between 0 and 100.I put that into the Outgoing Value Format but OpenHAB ignores it and just sends x.

version: 1
things:
  mqtt:topic:MQTTv2:WindowBlind:
    bridge: mqtt:broker:MQTTv2
    label: Window Blind
    location: Office
    channels:
      Moes:
        type: rollershutter
        label: Moes
        description: ""
        config:
          stateTopic: zb2mqtt/WindowBlind
          commandTopic: zb2mqtt/WindowBlind/set
          transformationPattern:
            - JSONPATH:$.position
          formatBeforePublish: "{\"position\": %s}"

If I use a Dimmer item it works fine (but it’s the wrong way round - 75% is the blind 25% open - 25% is the blind 75% open). I don’t understand why the Rollershutter item doesn’t comply - it seems to have been like this for years and people try to work round it

The challenge with using the pattern is the : and possibly the { and } and definitely the " all have specially meaning.
Usually the " " manages that but I’m my experience sometimes it doesn’t.

In fact, I’ve never been able to get JSON to work in the format before publishing field. But the Jinja transformation does work in all cases.

JINJA:{"position": {{value}}}

However, you need to put it in the command transformation instead. And you need to install the Jinja transform add-on.

Thanks Rick - I knew you would have an answer. I will try that. It doesn’t explain why ithe before publish works in a Dimmer but not in a Rollershutter which points to a bug (in the Rollershutter definition) going back to OH2.