Enabling/Disabling a Shelly Plus 2PM relay schedule from HA using a REST switch [Solution]

I have a Shelly Plus 2PM relay, where I have set up schedules on the Shelly to turn the relay on/of at certain times of the day. I prefer to do this on the device itself so that the schedule will run without being reliant on network connectivity or my Home Assistant server being operational.

The 2PM relay on/off switches show up automatically in HA, but there are no automatic entities to control the internal schedules.

I found this page here which gave a good starting point into how to create a REST switch and the Shelly API to manage this: Managing Shelly relay schedules remotely from Home Assistant – James Nimmo

However, this guide is for the first generation shelly relays, and does not work for the Shelly Plus 2PM. The 2PM uses the gen2 API here: Welcome! | Shelly Technical Documentation

For anyone else trying to do a similar thing, here are working REST switch configurations that enable/disable the first two schedules on a 2PM:

switch:
  - platform: rest
    name: "Irrigation.Fruit.On1"
    resource: http://192.168.50.171/rpc
    method: post
    headers:
      Content-Type: application/json
    body_on: '{"id":1,"method":"Schedule.Update","params":{"id":1,"enable":true}}'
    body_off: '{"id":1,"method":"Schedule.Update","params":{"id":1,"enable":false}}'
    state_resource: http://192.168.50.171/rpc/Schedule.List
    is_on_template: "{{ value_json['jobs'][0]['enable']  }}"
  - platform: rest
    name: "Irrigation.Fruit.Off1"
    resource: http://192.168.50.171/rpc
    method: post
    headers:
      Content-Type: application/json
    body_on: '{"id":1,"method":"Schedule.Update","params":{"id":2,"enable":true}}'
    body_off: '{"id":1,"method":"Schedule.Update","params":{"id":2,"enable":false}}'
    state_resource: http://192.168.50.171/rpc/Schedule.List
    is_on_template: "{{ value_json['jobs'][1]['enable']  }}"

In the above example, first I have created 2 schedules for the first relay using the Shelly 2PM web interface (accessible from a handy link from the HA device info on your shelly). The first schedule turns the relay on at a certain time, the second schedule turns it off at a certain time.

The code above creates two switches, the first which enables/disables the On schedule, the second which enables/disables the Off schedule, allowing me to turn the automatic scheduling on/off from HA.

Some quick notes on the code:

  • A separate resource and state_resource link is required, as in the 2PM setting the state and getting the state require 2 separate api calls
  • In the body_on/body_off the first id can be set to what you like. The second id is the important one - it must match the id of the schedule that you want to control. In the 2PM the schedules are numbered from values incrementing from 1. You can find the number for a particular schedule by clicking on in from the Shelly web interface. Note that these ids are global, rather than per relay.
  • The is_on_template extracts the enabled state from the Schedule.List API call json. In value_json[‘jobs’][X][‘enable’], X is an index into the array of the schedules, and so must be the id-1.
  • I don’t think the headers section is strictly required
  • Obviously you need to update the IP address to match your device, and you need to make sure you have static address assigned.
1 Like

Hi Bruce,

Your post has been a big help in (almost) getting a switch for my Shelly 1 Plus in Home Assistant. I think I am almost there, but I can’t seem to make the “is_on_template” part working, hence the whole switch won’t work. Hope you, or somebody else, can help me with this.

I would like to use a rest switch in order to change the “Attached to Output” mode on the Shelly from “Attached” (follow) to “Detached” or vice versa.

I found these URLs that do exactly what I need:

http://192.168.48.55/rpc/Switch.SetConfig?id=0&config={"in_mode":"follow"}
http://192.168.48.55/rpc/Switch.SetConfig?id=0&config={"in_mode":"detached","initial_state":"off"}

With the help of your post, the Shelly Technical Documentation and a HTTP tool I was able to test these body on and off types:

body_on: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"follow"}}}'
body_off: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"detached","initial_state":"off"}}}'

Status updates are provided using “Switch.GetConfig”.

When all put together in Home Assistant:

switch:
  - platform: rest
    name: "Shelly1Plus.Doorbell.Buzzer"
    resource: http://192.168.48.55/rpc
    method: post
    headers:
      Content-Type: application/json
    body_on: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"follow"}}}'
    body_off: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"detached","initial_state":"off"}}}'
    state_resource: http://192.168.48.55/rpc/Switch.GetConfig
    is_on_template: "{{ value_json['in_mode'] ['follow'] }}"

Whatever I try I always get a 'UndefinedError: ‘value_json’ is undefined error when testing the Template in Home Assistant.

I do get values back when using a POST request:

URL: http://192.168.48.55/rpc/Switch.GetConfig

Request Body:
{"id":0,"method":"Switch.GetConfig","params":{"id":0}}

Reply Body:
{
    "id": 0,
    "name": "Deurbel",
    "in_mode": "follow",
    "initial_state": "off",
    "auto_on": false,
    "auto_on_delay": 60,
    "auto_off": false,
    "auto_off_delay": 60
}

Any thoughts?

Hi, I think the problem is that in your is_on_template you are trying to use the term ‘follow’ as a key in to a two dimensional array (tying to access the value of the ‘follow’ key which is a child of the ‘in_mode’ key, but in your case ‘follow’ is the value, not another key) :

The result of the template within the {{ }} should evaluate to true or false. I think what you want is something like:

is_on_template: "{{ value_json['in_mode']  == 'follow' }}"

For further explanation, the json I was decoding for my code above was this:

is_on_template: "{{ value_json['jobs'][1]['enable']  }}"

All three elements in the square brackets are keys, taking me to a value which was boolean already, so I did not need to use == to evaluate it, but perhaps this would make it clearer:

is_on_template: "{{ value_json['jobs'][1]['enable']  == true }}"

For completeness, here is the json I was decoding:

{
    "jobs": [
        {
            "id": 1,
            "enable": false,
            "timespec": "0 0 5 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": true,
                        "id": 0
                    }
                }
            ]
        },
        {
            "id": 2,
            "enable": false,
            "timespec": "0 0 6 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": false,
                        "id": 0
                    }
                }
            ]
        },
        {
            "id": 3,
            "enable": false,
            "timespec": "0 0 18 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": true,
                        "id": 0
                    }
                }
            ]
        },
        {
            "id": 4,
            "enable": false,
            "timespec": "0 0 19 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": false,
                        "id": 0
                    }
                }
            ]
        },
        {
            "id": 5,
            "enable": false,
            "timespec": "0 1 6 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": true,
                        "id": 1
                    }
                }
            ]
        },
        {
            "id": 6,
            "enable": false,
            "timespec": "0 0 7 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": false,
                        "id": 1
                    }
                }
            ]
        },
        {
            "id": 7,
            "enable": false,
            "timespec": "0 0 17 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": true,
                        "id": 1
                    }
                }
            ]
        },
        {
            "id": 8,
            "enable": false,
            "timespec": "0 59 17 * * SUN,MON,TUE,WED,THU,FRI,SAT",
            "calls": [
                {
                    "method": "switch.set",
                    "params": {
                        "on": false,
                        "id": 1
                    }
                }
            ]
        }
    ],
    "rev": 114
}

Thanks for your quick reply!

It makes more sense now, but unfortunately it still won’t work. Whatever way I note it, it is still gives me the same UndefinedError: ‘value_json’ is undefined error.

I tried changing the

state_resource: http://192.168.48.55/rpc/Switch.GetConfig
to
state_resource: http://192.168.48.55/rpc/Switch.GetConfig?id=0

as according to the Home Assistant Documentation it uses GET to fetch the state. But still I can’t get the right format in to fetch the actual state.

Hi,

Yes, the shelly docs say that GET is used for the GetConfig, passing the id like you give above - see here: Switch | Shelly Technical Documentation

My Shelly Plus 2PM has the same interface. Using get in the format above I can get the json response.

I tried adding your code to my HA (changing the IP to mine), adding it to my config, then doing Developer Tools->Restart->Quick Reload, and adding the entity to a dash,

As expected, your very first example does not work. In my home-assistant.log is see:

[homeassistant.components.rest.switch] Got non-ok response from resource: 400

After playing about a bit, the following works for me (my ips):

switch:
  - platform: rest
    name: "Shelly1Plus.Doorbell.Buzzer"
    resource: http://192.168.50.171/rpc
    method: post
    headers:
      Content-Type: application/json
    body_on: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"follow"}}}'
    body_off: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"detached","initial_state":"off"}}}'
    state_resource: http://192.168.50.171/rpc/Switch.GetConfig?id=0
    is_on_template: "{{ value_json['in_mode'] == 'follow' }}"

The two key points are:

  • Use the http://192.168.50.171/rpc/Switch.GetConfig?id=0 link for the state_resource as you tried recently (updating the IP of course)
  • Copy the is_on_template from this recent example. My suggestion above had 2 spaces before the ==, which seems to cause problems (and pretty tricky to notice!)
1 Like

THAT’S IT! THANK YOU! :grinning:

Although the template editor in Home Assistant was still giving me the same error, I went ahead and pasted your code (with my IP’s) in my configuration.yaml and restarted, and it worked!

here is my final (working code):

switch:
  - platform: rest
    name: "Shelly1Plus.Doorbell.Buzzer"
    resource: http://192.168.48.55/rpc
    method: post
    headers:
      Content-Type: application/json
    body_on: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"follow""initial_state":"match_input"}}}'
    body_off: '{"id":1,"method":"Switch.SetConfig","params":{"id":0,"config":{"in_mode":"detached","initial_state":"off"}}}'
    state_resource: http://192.168.48.55/rpc/Switch.GetConfig?id=0
    is_on_template: "{{ value_json['in_mode'] == 'follow' }}"

(I added an initial_state to the body_on value.)

Thank you very much for your help, much appreciated.
I was staring at this code for far too long. :face_with_monocle:

2 Likes