OH3 Example ShellyPLUS1pm Things, Items, Rules

I own several Gen1 Shellies and I’m quite fond of how they work. So I ordered a few new ones and thought the software etc. is the same but they changed a lot. MQTT is way different and nothing worked like I wanted. After 2 days of research, I got them working as far as I needed them to. So this should be helpful for anyone using Gen2 Shellies like the ShellyPlus1pm for example.
In my example it is a ShellyPLUS 1pm (with Power Meter) for my air condition unit controlled by MQTT with Mosquitto and Openhab 3.

shellyplus.things

    Thing topic shellyplus1pm_dg_klimaanlage "Shellyplus1pm Klimaanlage" @ "DG" {
    Channels:
        Type switch : PowerSwitch     [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/switch:0" , transformationPattern="JSONPATH:$.output", on="true" , off="false" , commandTopic="shellies/shellyplus1pm_DG_Klimaanlage/rpc" , formatBeforePublish="{\"src\":\"openhab\",\"method\":\"Switch.Set\", \"params\":{\"id\":0,\"on\":%s}}" ] 
        Type string : Event0          [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/events/rpc", transformationPattern="REGEX:(.*\"event\":.*)∩JSONPATH:$.params.events[*].event" ] 
        Type string : Source          [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/switch:0" , transformationPattern="JSONPATH:$.source" ]
        Type number : Power           [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/switch:0" , transformationPattern="JSONPATH:$.apower" ]
        Type number : Voltage         [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/switch:0" , transformationPattern="JSONPATH:$.voltage" ]
        Type number : Energy          [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/switch:0", transformationPattern="JSONPATH:$.aenergy.total∩JS:shellyplus_energy_kwh.js" ] // From Shelly in Wh
        Type number : Temperature     [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/switch:0", transformationPattern="JSONPATH:$.temperature.tC" ]
        Type string : UpdAvailable    [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/sys",  transformationPattern="REGEX:(.*stable.*)∩JSONPATH:$.available_updates.stable.version"]
        Type string : RestartReq      [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/sys",  transformationPattern="REGEX:(.*restart_required.*)∩JSONPATH:$.restart_required"]
        Type number : Uptime          [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/status/sys",  transformationPattern="REGEX:(.*uptime.*)∩JSONPATH:$.uptime"] // in seconds
        Type string : IP              [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/shellystatus/rpc", transformationPattern="REGEX:(.*sta_ip.*)∩JSONPATH:$.result.wifi.sta_ip"]
        Type string : Devicestate     [ stateTopic="shellies/shellyplus1pm_DG_Klimaanlage/online" ]
      }

(The backslashes are to escape " in the JSON string)

To get all the status informations like in Gen1 you have to do 2 things.

  1. You have to enable MQTT with your Mosquitto settings and ENABLE “Generic status update over MQTT” in the UI.
    Shelly WebUI MQTT configuration
  2. We need a rule to trigger a MQTT message sent to the Shelly, so that it sends more status updates back.

shellyplus_start.rules

val logName = "shellyplus_start.rules"

rule "Get Status"

when
    Member of g_shellyplus_source changed to "init" or
    Member of g_shellyplus_event changed to "scheduled_restart"
then
    val shelly = triggeringItem.name.toString().replace('_source', '')
    logInfo(logName, "Shelly: " + shelly)

    var actionsBroker = getActions("mqtt","mqtt:broker:mosquitto")
    actionsBroker.publishMQTT( "shellies/" + shelly + "/rpc", "{\"id\":1, \"src\":\"shellies/" + shelly + "/shellystatus\", \"method\":\"Shelly.GetStatus\"}" )
end

The rule sends a message to the /rpc commandtopic and the Shelly then sends infos back. The Shelly creates a new subtopic for the answer which you can define with “src”. Yes, really. I didn’t understand that part neither in the beginning but it is not as complicated as you would think.
Here in our example all my Shellies have the overall topic shellies/ and then the Shelly name. You can see that in a program like MQTT explorer.

When we now restart the Shelly the rule is triggered and sends the payload

{"id":1, "src":"shellies/shelly1pmplus_DG_Klimaanlage/shellystatus", "method":"Shelly.GetStatus"}

to the channel

shellies/shelly1pmplus_DG_Klimaanlage/rpc

Some of the methods can be found in the Allterco Shelly documentation. The GetStatus method is described here.
Some methods are not explained for MQTT but it seems to work like the cUrl commands with the same arguments.
Now, because we set our “src” to shellies/shelly1pmplus_DG_Klimaanlage/shellystatus the Shelly will send its answer to this subtopic!!!

All the missing status items from Gen1 Shellies are in this answer.

Now to the different channels in our things file.
The PowerSwitch was a bit tricky because the Gen2 API requests a JSON string back in the command topic. Also it is not on and off as it was but true and false.
In our stateTopic we receive the state for the relay. The topic payload is in JSON so we have to do JSONPATH as transformationPattern to get output from the JSON. The ouput if true or false but because we have a Switch Thing we need on or off. This can be accomplished by using

on="true" , off="false"

The commandTopic is a little bit more complicated because we have to send a JSON payload back. The payload for a command is

{"src":"openhab","method":"Switch.Set", "params":{"id":0,"on":true}

The params part can be true or false and switches the relay on or off. Because of our

on="true" , off="false"

We can use %s in our format string to make on=true and off=false. So we use formatBeforePublish to switch between true and false because the rest of the payload is always the same.

formatBeforePublish="{"src":"openhab","method":"Switch.Set", "params":{"id":0,"on":%s}}"

So, on every toggle from our switch every command will have the command “on” or “off” send which will be substituted on the %s position with “true” or “false”.
So, when switching “ON” normally the command on would be send to shellies/shellyplus1pm_DG_Klimaanlage/rpc but because of the formatBeforePublish the “ON” will be substituted with {“src”:“openhab”,“method”:“Switch.Set”, “params”:{“id”:0,“on”:true}}. “OFF” will be substituted to {“src”:“openhab”,“method”:“Switch.Set”, “params”:{“id”:0,“on”:false}}

If anybody is searching for another purpose to TOGGLE a switch, the new Gen2 payload would be

{"method":"Switch.Toggle","params":{"id":0}}

The next channels are quite self explaining. They’re all different things from the Shelly which we’ll get from different topics. We have to pay attention that not all things are there all the time. An event (pushing a button) is only in the payload if it’s actually pushed and then disappears. So we have to check with a REGEX transformation if the payload actually exists

REGEX:(.*\"event\":.*)

checks if “event” is in the payload. If yes, ∩ sends it to the next transformation which here is JSONPATH and with

JSONPATH:$.params.events[*].event"

Gets the event from the payload. For the Shellies this could be btn_down, btn_up, single_push, long_push, double_push, scheduled_restart.

The only other thing is, energy from the Shelly is in Watthours but I like it in Kilowatthours or kWh. This channel gets the energy number from the Shelly and then sends it to a Javascript file to transform it.

shellyplus_energy_kwh.js

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("shellyplus_energy_kwh.js");

(function(i){
    //logger.warn(i);
    x = (i/1000).toFixed(2);
    //logger.warn(x);
    return x
})(input)

If you comment the logger.warn in it will show in the Openhab logs. But it’s annoying fast. So just for debugging!

Uptime from the Shelly is in seconds! If you want to transform that, just use another .js file and do a little math.

You can get every channel without the rule from the beginning except the IP address. I don’t know why but the IP is in no status topic from the Shelly, except you ask explicitly for it. This was different in Gen1. So if you are not interested in the Shelly IP, you don’t need the shellyplus_start.rules from the beginning!!!

This is all I need for my purposes with the Shelly. So, to use it I created the corresponding
shellyplus.items

Group        g_shellyplus_source
Group        g_shellyplus_event

Switch shellyplus1pm_DG_Klimaanlage_R1                 "DG Klimaanlage"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:PowerSwitch" }
String shellyplus1pm_DG_Klimaanlage_event0             "DG Klimaanlage Tasterevent" (g_shellyplus_event) // btn_down, btn_up, single_push, long_push, double_push, scheduled_restart
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Event0" }
String shellyplus1pm_DG_Klimaanlage_source             "DG Klimaanlage Eingabe" (g_shellyplus_source)
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Source" }
Number shellyplus1pm_DG_Klimaanlage_power              "DG Klimaanlage Power [%s W]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Power" }
Number shellyplus1pm_DG_Klimaanlage_voltage            "DG Klimaanlage Voltage [%s V]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Voltage" }
Number shellyplus1pm_DG_Klimaanlage_energy             "DG Klimaanlage Energy [%s kWh]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Energy" }
Number shellyplus1pm_DG_Klimaanlage_temperature        "DG Klimaanlage Temperatur"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Temperature" }
String shellyplus1pm_DG_Klimaanlage_ip                 "IP Adresse [%s]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:IP" }
String shellyplus1pm_DG_Klimaanlage_updavailable       "FW Update [%s]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:UpdAvailable" }
String shellyplus1pm_DG_Klimaanlage_restartreq         "Neustart notwendig [%s]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:RestartReq" }
Number shellyplus1pm_DG_Klimaanlage_uptime             "Uptime [%s s]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Uptime" }
String shellyplus1pm_DG_Klimaanlage_state              "Status [%s]"
    { channel="mqtt:topic:mosquitto:shellyplus1pm_dg_klimaanlage:Devicestate" [profile="transform:MAP", function="shellyplus.map"] }

The 2 groups are to differentiate for the shellyplus_start.rules file if a restart happened. As said above, if you’re not interested in the IP you don’t need the 2 groups.

The last item is mapped so that the sitemap show the state as “online” or “offline” instead “true” or “false”

shellyplus.map

true=online
false=offline

To test and see all our values I also did a shellyplus.sitemap

shellyplus.sitemap

sitemap shellyplus label="Shelly Plus" { 

	Frame label="Shelly Plus 1PM" {
        Switch item=shellyplus1pm_DG_Klimaanlage_R1
        Text   item=none                         label="" icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_event0 icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_source icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_power icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_voltage icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_energy icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_temperature icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_ip icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_updavailable icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_restartreq icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_uptime icon=none
        Text   item=shellyplus1pm_DG_Klimaanlage_state icon=none
	} 
}

I am not sure if I like the PLUS series. I have a lot of Gen1 devices and they worked flawlessly and the MQTT integration was easy and understandable. This Gen2 seems a little overengeneered and overcomplicated to me. I hope they knew what they were doing…
I hope this answers a lot of questions for people who are trying to get a Shelly PLUS running. This took me 2-3 days to get all the different informations I needed to get the Shelly doing what I wanted it to do!

1 Like

There is a Shelly binding. It provides auto discovery and local control. If your devices are supported by the binding, it’s definitely the easiest way to go.

I know. Thanks for the info. I did it file based from the beginning and I liked it. I followed the whole development thread for the binding. I just thought it’s enough if one person has to search all the infos to do it by MQTT.