Shelly Bulb via MQTT 2.4

That other topic cleary says:

  • MQTT state topic: shellies/shelly1-123ABC/relay/0
  • MQTT command topic: shellies/shelly1-123ABC/relay/0/command
1 Like

Thank you so much - this is the solution :slight_smile:


For future reference, here the full description of the Shelly Bulb MQTT API

excerpt from link above:
shellies/shellybulb-<deviceid>/color/0/command accepts on and off payloads
shellies/shellybulb-<deviceid>/color/0 is used by the device to report its current on-off state

For controlling other parameters of the LED channels publish to
Subscribers can use this to obtain the latest device state.
for format of the json and all options see link above

1 Like

Now I would like to use the color picker:

Color Shellybulb1Color "shellybulb1_color" (Gtest) {channel="mqtt:topic:xyz:shellybulb1_color"}

The channel for " shellybulb1_color" is:

MQTT state topic: shellies/shellybulb-xyz/color/0
MQTT command topic: shellies/shellybulb-xyz/color/0/set

Could you please help me with JSON payload.

According to the Shelly doc

Device expects a JSON payload on this topic, with the following sample contents:

    "ison": false,   /* bool, can be set with on and off commands */
    "mode": "color", /* "color" or "white" */
    "red": 0,        /* red brightness, 0..255, applies in mode="color" */
    "green": 0,      /* green brightness, 0..255, applies in mode="color" */
    "blue": 255,     /* blue brightness, 0..255, applies in mode="color" */
    "white": 0,      /* white brightness, 0..255, applies in mode="color" */
    "gain": 100,     /* gain for all channels, 0..100, applies in mode="color" */
    "temp": 4750,       /* color temperature in K, 3000..6500, applies in mode="white" */
    "brightness": 100,  /* brightness, 0..100, applies in mode="white" */
    "effect": 0 /* applies an effect when set */

When setting the color to red in the web interface MQTT.fx tells me:

shellies/shellybulb-XYZ/color/0/status --> {"ison":true,"mode":"color","red":255,"green":0,"blue":0,"white":0,"gain":100,"temp":4015,"brightness":100,"effect":0}

Where do I have to put that code above in? Is it for “outgoing value format”?

Your question sounds as you may not be familiar with rules…is so please read the documentation before you proceed.
I have everything defined in a text file, so you will need to adjust if you configure things and items in PaperUI (and I cannot help a lot there as I am not using it). Finally, do NOT mix textual configuration and configuration in text files.
So here my thing definition:

Bridge mqtt:broker:mosquitto "Mosquitto" @ "Systems" [ host="", secure=false ]	
	Thing topic Shelly_Down_front_left "Bulb Front left" @ "Downstairs" {
		Type string : Power "Switch" [ stateTopic="shellies/shellybulb-3CC48E/color/0", commandTopic="shellies/shellybulb-3CC48E/color/0/command" ]
		Type string : Shelly_status "Status" [ stateTopic="shellies/shellybulb-3CC48E/color/0/status", commandTopic="shellies/shellybulb-3CC48E/color/0/set" ]

My corresponding items:

String Down_front_left_proxy "Proxy for Wall Downstairs Front left" {channel="mqtt:topic:mosquitto:Shelly_Down_front_left:Power"}
Switch Down_front_left "Wall Downstairs Front left" (gShelly_power, gShelly_lamps, gTwilightLightsON, gTwilightLightsOFF, gWeatherTwoLightsON, gWeatherTwoLightsOFF, gDownstairs,
        gMorningLightsON, gMorningLightsOFF, gLights,gLightPanel,gEveningLightsON, gNightLightsOFF) // {channel="mqtt:topic:mosquitto:Shelly_Down_front_left:Power"}
String Down_front_left_status "Status bulb down front left" (gShelly_status) {channel="mqtt:topic:mosquitto:Shelly_Down_front_left:Shelly_status"}
Color Down_front_left_color "Wall Downstairs Front left Color" (gShelly_color, gShelly_lamps)
Switch Down_front_left_mode "bulb mode" (gShelly_mode, gShelly_lamps) //white or color
Dimmer Down_front_left_dim "Brightness" (gShelly_dim, gShelly_lamps)
Dimmer Down_front_left_coldim "Color Temperature" (gShelly_coldim, gShelly_lamps)

My Rules:
Maybe a quick remark: the code is not optimized and I am sure can be improved. I have four bulbs and I wrote one rule for each function that works for all four; however, it is relying on groups and naming schemes.
I left a lot of commented out login statements in here that I use to debug, you can delete as you wish.
A lot of the rules deals with parsing the JSON, and more importantly putting it back together.
You will need the JSON transform loaded in your system.

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "shelly Sitemap to MQTT"
	Member of gShelly_power received command
    //logInfo("Sitemap to MQTT", "rule triggered, triggering item: " +
    //logInfo("Sitemap to MQTT", "Item to be used: " + ScriptServiceUtil.getItemRegistry.getItem( +"_proxy").name.toString)
    //val proxy = +"_proxy"
    //val testItem = ScriptServiceUtil.getItemRegistry.getItem(proxy)
	if (triggeringItem.state == ON ) {//sendCommand( +"_proxy", "on")
        ScriptServiceUtil.getItemRegistry.getItem( +"_proxy").sendCommand("on")
    if (triggeringItem.state == OFF ) {
        ScriptServiceUtil.getItemRegistry.getItem( +"_proxy").sendCommand("off")
    Thread::sleep(10)                    //wait for persistence to catch up

rule "HSB value to RGB color value"
	Member of gShelly_color received command 
    if ((triggeringItem == NULL) || (triggeringItem == UNDEF))
    //logInfo("Colorpicker", "colorpicker" + Down_front_left_color.state.toString)
	val hsbValue = triggeringItem.state as HSBType
    val brightness = hsbValue.brightness.intValue 
	val redValue = (((( * 255) / 100) *brightness) /100).toString 
	val greenValue = (((( * 255) / 100) *brightness) /100).toString
	val blueValue = (((( * 255) / 100) *brightness) /100).toString
    val String new_color = "\"red\":" + redValue + ",\"green\":" + greenValue + ",\"blue\":" + blueValue 
    val trig_name_base ="_color").get(0)
    val trig_base = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base)
    val trig_status = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_status")
    //logInfo("Colorpicker", "original status: "+trig_status.state.toString)
    //logInfo("colorpicker", "This is the new color: "+ new_color)
    var String new_status = trig_status.state.toString.split("\"red\"").get(0) + new_color +  ",\"white\"" + trig_status.state.toString.split("\"white\"").get(1)
    new_status = new_status.split("\"mode\"").get(0) + "\"mode\":" + "\"color\"" + ",\"red\"" +  new_status.split("\"red\"").get(1)
    //logInfo("colorpicker", "New Status: "+new_status)
    Thread::sleep(10)                    //wait for persistence to catch up

rule "set colortemp and brightness"
    Member of gShelly_coldim received command or 
    Member of gShelly_dim received command 
    if ((triggeringItem == NULL) || (triggeringItem == UNDEF))
    //logInfo("brightness", "Dimmer value is: " + Down_front_left_dim.state.toString)
    //logInfo("brightness", "Colortemp is: " + Down_front_left_coldim.state.toString)
    var String trig_name_base
    if ("_coldim")) {
        trig_name_base ="_coldim").get(0)
        else trig_name_base ="_dim").get(0)
    val trig_base = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base)
    val trig_status = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_status")
    val trig_coldim = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_coldim")
    val trig_dim = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_dim")
    val color_temp = new DecimalType(trig_coldim.state.toString) * 35 +3000 
    //logInfo("brightness", "New colortemp is: " + color_temp.toString)
    var String new_status = trig_status.state.toString.split("\"temp\"").get(0) + "\"temp\":" + color_temp.toString + ",\"brightness\":" + trig_dim.state.toString + ",\"effect\"" + trig_status.state.toString.split("\"effect\"").get(1)
    new_status = new_status.split("\"mode\"").get(0) + "\"mode\":" + "\"white\"" + ",\"red\"" +  new_status.split("\"red\"").get(1)
    //logInfo("brightness", "old status is: " + Down_front_left_status.state.toString)
    //logInfo("brightness", "new status is: " + new_status)
    Thread::sleep(10)                    //wait for persistence to catch up

rule "color or white mode"
    Member of gShelly_mode received command
    //logInfo("CWmode", "triggeringItem: " +
    val trig_name_base ="_mode").get(0)
    //logInfo("CWmode", "trig_name_base = " + trig_name_base)
    val trig_base = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base)
    //logInfo("CWmode", "first one down")
    val trig_status = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_status")
    if (triggeringItem.state == OFF) {
        trig_status.sendCommand(trig_status.state.toString.split("\"mode\"").get(0) + "\"mode\":" + "\"white\"" + ",\"red\"" + trig_status.state.toString.split("\"red\"").get(1))
    if (triggeringItem.state == ON) {
        trig_status.sendCommand(trig_status.state.toString.split("\"mode\"").get(0) + "\"mode\":" + "\"color\"" + ",\"red\"" + trig_status.state.toString.split("\"red\"").get(1))
    Thread::sleep(10)                    //wait for persistence to catch up

rule "Update OH from Shellies"
    Member of gShelly_status changed
    //logInfo("update", "triggering Item: " +
    val trig_name_base ="_status").get(0)
    //logInfo("update", "trig_name_base = " + trig_name_base)
    val trig_base = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base)
    val trig_coldim = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_coldim")
    val trig_dim = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_dim")
    val trig_mode = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_mode")
    val trig_color = ScriptServiceUtil.getItemRegistry.getItem(trig_name_base + "_color")
    val json = triggeringItem.state.toString
    //logInfo("update", "json is: " + json)
    //logInfo("update", "temp: " + transform("JSONPATH", "$.temp", json))
    //logInfo("update", "Updating temp with: " + Math.round((Float::parseFloat(transform("JSONPATH", "$.temp", json))-3000)/35))
    trig_coldim.postUpdate(Math.round((Float::parseFloat(transform("JSONPATH", "$.temp", json))-3000)/35)) 
    trig_dim.postUpdate(Integer::parseInt(transform("JSONPATH", "$.brightness", json)))
    if ("white" == transform("JSONPATH", "$.mode", json)) trig_mode.postUpdate(OFF) 
    if ("color" == transform("JSONPATH", "$.mode", json)) trig_mode.postUpdate(ON) 
    trig_color.postUpdate(HSBType.fromRGB(Integer::parseInt(transform("JSONPATH", "$.red", json)) , Integer::parseInt(transform("JSONPATH", "$.green", json)) , Integer::parseInt(transform("JSONPATH", "$.blue", json))))
    if ("true" == transform("JSONPATH", "$.ison", json)) trig_base.postUpdate(ON) else trig_base.postUpdate(OFF)

As I said earlier, not pretty but it works for me.
and just for completeness, here my sitemap entries:

Switch item=Down_front_left
Switch item=Down_front_left_mode mappings=[ON="Color",OFF="White"] 
Colorpicker item=Down_front_left_color  visibility=[Down_front_left_mode==ON]
Slider item=Down_front_left_dim  visibility=[Down_front_left_mode==OFF]
Slider item=Down_front_left_coldim  visibility=[Down_front_left_mode==OFF]

Visibility is used to display colorpicker only in color mode and color temperature and intensity only in white mode.

Hope that helps

1 Like

@lipp_markus: Thanks a lot!

I’ll look into this. I wasn’t aware that one need so many code lines just to set the RGB values …
Do I have to install this addon first, right?
“JSONPath Transformation”

That’s because you need outgoing transformations, and they are not yet in (probably in a few days though).
And to ship around that shortcoming, you are forced to use full blown rules for the moment. If you can wait for another week, a shorter solution will be doable as well.

Cheers, David

1 Like

Yes, you will need to install the JSONPATH transformation.

Sounds promising. Will there be a dedicated shelly binding?

Nope, I was talking to the shelly devs, to use a MQTT convention like Homie instead. They have not responded / forgotten about it. But that is the right way to go. Is their MQTT support still “experimental”? Then it’s still possible to convince them probably.

1 Like

So let’s keep fingers crossed :wink:

Allterco guy here. Homie is one among many attempts to “standartize” the IoT – very OpenHAB-specific, so not really a widely adopted standard per se. MQTT in shellies tries to be the simplest possible functional interface to allow for easy integration with any system, not just openHAB. HomeAssistant have their own thing with the discovery mechanism, probably many other “standards” exist. Truth it, you can’t really do without a “plugin” of sorts on your home automation orchestrator. So, it is very unlikely that we ever implement Homie for Shellies.

I tried reading through the topic to understand what the problem is, but I’d appreciate it if someone summarizes the issue. Can we maybe make tiny changes to the MQTT interface to facilitate integration?

Basically, use shellies/shellybulb-<deviceid>/color/0/set to control the device and shellies/shellybulb-<deviceid>/color/0/status to subscribe for status updates. Both topics work with json payloads, input and output format should be identical.

1 Like

You are very close to the homie topic layout, have you noticed that? But you are missing self-describing information, like a type (string, integer, boolean) for topic values etc. And names for the device itself and its functions.
Without those a (generic) discover mechanism of any sorts is not really feasible.

I am looking at

and it doesn’t seem trivial to add this without a) a fair amount of effort and b) remaining backward-compatible.

Can we maybe provide a static registry of sorts not hosted on the devices themselves, to be used for discovery and configuration? I haven’t played with openHAB so excuse the ignorance…

Homie tries to stay dependency free, that’s why there is no json used. You guys have chosen to compact some states into json objects. But that doesn’t prevent you from becoming Homie compliant nevertheless, with a trick.
(That is: extensions)

But for easy integration I really need some sorts of:

  • shelly/device-id/$name
  • shelly/device-id/$ready (published as last-will, to identify when a shelly went offline)
  • shelly/device-id/$nodes (a comma separated list of features. We call those properties in Homie. I guess that can be made available in a static registry)

For what we call nodes in Homie:

  • shelly/device-id/color/$name (a name for this feature -> We need a presentable string for the user)
  • shelly/device-id/color/$properties (a list of sub-topics, comma separated, like “0” in your color case. Again a candidate for a central registry)

And you even have something like properties:

  • shelly/device-id/color/0 (In Homie we have the status directly published to this topic, you are using “status” instead. It would be super helpful if you publish to this topic as well, I guess)
  • shelly/device-id/color/0/set (that’s perfect, we are using this topic as well in Homie for setting a value)

For being discoverable you need a name though like:

  • shelly/device-id/color/0/$name (=“First bulb”)
  • shelly/device-id/color/0/$type (and that is were we apply the magic. Use “json-shelly” as type and just keep your json format for /set and the value topic and done, you are homie compliant. I mean, almost.)

If you decide to add those little meta topics, if you add two more, you are actually completely Homie compliant. That is:

  • shelly/device-id/$homie (=4.0)
  • shelly/device-id/$extensions (=“shelly”)

That’s my proposal. I would add the “shelly” extension to the Homie webpage and OH2 MQTT binding.

Cheers, David


Hi there,

Any news on this?

Is it the case that outgoing transformations are there, but just for files and not for the PaperUI?

I was able to find the incoming transformations, and with a simple made the incoming stat events update the state of the item:

(function(i) {
    shellyInput = JSON.parse(i);
    return + "," + + "," +;

But I’m struggling to find where to put the outbound transformation in PaperUI.

I can manage to code and go to the lower level but I’d rather stay within PaperUI as much as I can to keep the target of users of my openhab as wide as possible.

Surely not. The entire xtend file configuration nonsense (read: .thing and .item files) is not supported by me. You can be sure that every binding that is maintained by me works in Paper UI. You need OH 2.5M1 though.

1 Like