Basic Restful Switch Help

Hello,

I was just trying to setup a RESTful Switch entity, but I’m failing. I can call my switch like this:

curl -i -H "Content-Type: application/json" -d '{"switch_binary":{"value":1}}' http://ip:port/api/v1/nodes/14

That makes it turn on and a 0 makes it turn off, pretty simple. The output from the above command is:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 293
Server: TornadoServer/3.2.2

{
  "body": {
    "switch_binary": {
      "value": 1
    }
  }, 
  "params": {}, 
  "response": "0.00311", 
  "result": {
    "updated": {
      "switch_binary": {
        "value": 1
      }
    }
  }, 
  "url": "http://ip:port/api/v1/nodes/14", 
  "url_path": "/api/v1/nodes/14"

So I thought I’d be able to set a RESTful switch like this:

 # Switches
 switch:
   - platform: rest
     name: test_restful
     resource: http://ip:port/api/v1/nodes/14
     body_on: '{"switch_binary":{"value":1}}'
     body_off: '{"switch_binary":{"value":0}}'
     is_on_template: '{{ value_json.result.switch_binary.value }}'
     headers:
       Content-Type: application/json

However, when I load the interface, the switch is off, but the light is on. Turning the switch results in it looking on, then falling back to off, but no change in the actual light. I see nothing in the log that says it’s failing, or why.

Am I missing something really basic about the RESTful switch option? Thanks!

It doesn’t take the output from sending the body_on or body_off POST request, but rather does a separate GET request to the resource URL periodically (and evaluates the is_on_template each time) to update the state of the switch. So what does:

curl -H "Content-Type: application/json" http://ip:port/api/v1/nodes/14

return?

Oh ok, shoot. I was hoping it took the return because right now I have these switches working (not as restful switches), but there’s a delay after I turn it on and when the interface shows it as on. That command outputs:

HTTP 1.1 200 OK
Content-Type: application/json
Content-Length: 293
Server: TornadoServer/3.2.2

{
  "body": {
    "switch_binary": {
      "value": 1
    }
  },
  "params": {},
  "response": "0.00631",
  "result": {
    "updated": {
      "switch_binary": {
        "value": 1
      }
    }
  },
  "url": "http://ip:port/api/v1/nodes/14",
  "url_path": "/api/v1/nodes/14"

Edit: sorry what I pasted above is after an update. This is from a straight request (pi@:~ $ curl http://ip:port/api/v1/nodes/14 - I keep getting an error posting from my phone…

  "params": {},
  "response": "0.00426",
  "result": {
    "association": {
      "1": {
        "command_list": {
          "ccids": [
            "basic_report",
            "meter_report",
            "23041"
          ]
        },
        "info": {
          "profile": 1
        },
        "name": {
          "name": "Lifeline"
        },
        "nodes": {
          "maxnode": 5,
          "nodes": [
            1
          ]
        }
      }
    },
    "association_groupings": {
      "groupings": 1
    },
    "basic": {
      "value": 100
    },
    "configuration": {},
    "health": {
      "command_ack_time": [
        0.031056767471502055,
        "s",
        "green"
      ],
      "command_error_rate": [
        1.1590663783732357e-71,
        "%",
        "green"
      ],
      "command_response_time": [
        0.017089494869207245,
        "s",
        "green"
      ],
      "commands_per_minute": [
        11.109367326277257,
        "",
        "green"
      ],
      "log": 0,
      "report_dup_rate": [
        47.4614689360484,
        "%",
        "red"
      ],
      "reports_per_hour": [
        51.66559237199424,
        "",
        "green"
      ],
      "response_dup_rate": [
        10.653911844376227,
        "%",
        "red"
      ],
      "response_error_rate": [
        0.0,
        "%",
        "green"
      ],
      "response_success_rate": [
        100.02079381219863,
        "%",
        "green"
      ]
    },
    "ident": {
      "address": "0:14",
      "name": "Living Room - Left"
    },
    "manufacturer_specific": {
      "manufacturer_id": "zooz",
      "product_id": 13,
      "product_type_id": 257
    },
    "meter": {
      "A": {
        "meter": "electric",
        "precision": 3,
        "rate": "import",
        "scale": "A",
        "size": 4,
        "value": 0.114
      },
      "V": {
        "meter": "electric",
        "precision": 3,
        "rate": "import",
        "scale": "V",
        "size": 4,
        "value": 124.63
      },
      "W": {
        "meter": "electric",
        "precision": 3,
        "rate": "import",
        "scale": "W",
        "size": 4,
        "value": 9.348
      },
      "kWh": {
        "meter": "electric",
        "precision": 3,
        "rate": "import",
        "scale": "kWh",
        "size": 4,
        "value": 0.474
      }
    },
    "meter_supported": {
      "meter": "electric",
      "reset": 1,
      "scales": [
        "kWh",
        "W",
        "V",
        "A"
      ]
    },
    "node_info": {
      "cmdclasses": [
        "zwaveplus_info",
        "switch_binary",
        "meter",
        "switch_all",
        "scene_actuator_conf",
        "scene_activation",
        "configuration",
        "association",
        "association_grp_info",
        "manufacturer_specific",
        "version",
        "security",
        "firmware_update_md",
        "powerlevel",
        "device_reset_locally"
      ],
      "controlclasses": []
    },
    "protocol": {
      "basic_type": "routing_slave",
      "cap": 83,
      "generic_type": "switch_binary",
      "listening": true,
      "security": 28,
      "sensor1000": false,
      "sensor250": false,
      "specific_type": "power_switch_binary"
    },
    "switch_binary": {
      "value": true
    },
    "version": {
      "application": 1,
      "application_subversion": 3,
      "library": "slave_enhanced",
      "protocol": 4,
      "protocol_subversion": 38
    },
    "version_command": {
      "association": {
        "version": 2
      },
      "association_grp_info": {
        "version": 1
      },
      "configuration": {
        "version": 1
      },
      "device_reset_locally": {
        "version": 1
      },
      "firmware_update_md": {
        "version": 2
      },
      "manufacturer_specific": {
        "version": 2
      },
      "meter": {
        "version": 3
      },
      "powerlevel": {
        "version": 1
      },
      "scene_activation": {
        "version": 1
      },
      "scene_actuator_conf": {
        "version": 1
      },
      "security": {
        "version": 1
      },
      "switch_all": {
        "version": 1
      },
      "switch_binary": {
        "version": 1
      },
      "version": {
        "version": 2
      },
      "zwaveplus_info": {
        "version": 2
      }
    }
  },
  "url": "http://ip:port/api/v1/nodes/14",
  "url_path": "/api/v1/nodes/14"

Well, then it should work. Assuming, of course, that there is a { at the beginning and a } at the end that you just didn’t copy into your post above.

Sorry, what do you mean by that?

Do you mean when you turn it on not via HA? If so, then that’s to be expected, because the RESTful Switch (like many components) works by periodic polling. You can minimize the delay by reducing the polling interval (via the scan_interval config variable.)

Yes, you’re right the switching on function does work if I only interact with it in HA and I start with the light off.

If the light is on, which is what I had because I was sitting in the room at night, the restful switch says it’s off and hitting the switch doesn’t make the switch stay, and doesn’t make the light change.

If I start with the light off, and hit the restful switch, the light turns on, the switch in HA moves to on briefly, then back to off. At this point, the restul switch is unable to turn the light off.

When I mentioned I had these working not as restful switches, what I mean is that I’ve built a rest_command service entry in my configuration.yaml. This uses a different function of the API, which I’d prefer not to use (the alpha000 path) because I assume at some point it’ll stop being functional in preference for the api/v1 path. It’s also not doing a real POST command, which I’d prefer.

#test turning on lights
rest_command:
  light_set:
    url: http://ip:port/alpha000/basic_set?node={{ node }}&value={{ value }}
    method: GET

Then built sensors:

# Sensors
sensor:
  - name: living_room_left_status
    platform: rest
    resource: http://ip:port/alpha000/dump_node?node=14
    method: GET
    value_template: '{{ value_json.result.switch_binary.value }}'
    force_update: true

And then this is my switch entry:

# Switches
switch:
  - platform: template
    switches:
      living_room_left:
        friendly_name: "Living Room - Left"
        value_template: "{{ is_state('sensor.living_room_left_status', 'True') }}"
        turn_on:
          service: rest_command.light_set
          data:
            node: 14
            value: 100
        turn_off:
          service: rest_command.light_set
          data:
            node: 14
            value: 0

I’m fairly new to HA and am certainly learning on the move. I got this to work and was happy, but it feels overly complicated, which is what I was hoping the restful switch would solve. I’ll look into the scan_interval variable. Is there not a way to get HA to read the returned body when calling an API?

I haven’t reviewed the switch/rest.py code lately, but I suspect what might be happening is, when you tell HA to turn the light on it sends the POST request with the body_on data, and then immediately turns around and does a GET to check the state. But maybe the device takes a small amount of time to update its state, and the GET is getting the old state – i.e., when it was still off. But then on the next update HA should get the correct state and the HA entity should update to show that the light is on. If that’s not happening, then you need to check the logs to see what’s going on.

Yes. You simply modify the code to make it do what you want. :wink:

But, seriously, I think you’re on to the correct solution with the template switch. One thing that might make it better (if there’s still a delay between turning the light on or off via HA and the switch entity updating to indicate that) is to add a step to both the turn_on and turn_off actions to force sensor.living_room_left_status to update itself. That template sensor also updates periodically (with the interval controlled by scan_interval), but you can force it to update using the homeassistant.update_entity service.

# Switches
switch:
  - platform: template
    switches:
      living_room_left:
        friendly_name: "Living Room - Left"
        value_template: "{{ is_state('sensor.living_room_left_status', 'True') }}"
        turn_on:
          - service: rest_command.light_set
            data:
              node: 14
              value: 100
          - service: homeassistant.update_entity
            entity_id: sensor.living_room_left_status
        turn_off:
          - service: rest_command.light_set
            data:
              node: 14
              value: 0
          - service: homeassistant.update_entity
            entity_id: sensor.living_room_left_status

If you need a small amount of delay between the two service calls, then add a delay step:

        turn_on:
          - service: rest_command.light_set
            data:
              node: 14
              value: 100
          - delay: 1
          - service: homeassistant.update_entity
            entity_id: sensor.living_room_left_status

Thanks for your help!

I setup the second service entry to update the sensor, but I’m not sure if it’s doing what I expect. When I change the switch in the UI the light indeed turns on/off. But the switch still ‘resets’ to what it was for a few seconds, before then showing the proper status. I turned logged to DEBUG and grabbed this when I turned the switch from On to Off (edit: never mind, I see this says living_room_right, which is not the switch I toggled. I’m working with living_room_left as described before.)

2019-03-04 10:36:09 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: old_state=<state sensor.living_room_right_status=False; friendly_name=living_room_right_status @ 2019-03-04T10:35:38.051255-08:00>, new_state=<state sensor.living_room_right_status=False; friendly_name=living_room_right_status @ 2019-03-04T10:36:09.318477-08:00>, entity_id=sensor.living_room_right_status>

That looks right to me, but there’s still a few second delay in the UI to show the proper status of the switch, and it means if I turn the light on, I have to wait a few second the turn it off. That was with the delay set to 1, without the delay line, I see this. It felt like about a 30 second delay between when I hit the switch and saw the switch toggle in the UI, and I think this confirms it. I’m guessing 10:44:01 is when I hit the switch and 10:44:37 is when the toggle changed:

2019-03-04 10:44:01 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: domain=switch, service=turn_on, service_data=entity_id=switch.living_room_left>

2019-03-04 10:44:01 INFO (MainThread) [homeassistant.helpers.script] Running script

2019-03-04 10:44:01 INFO (MainThread) [homeassistant.helpers.script] Executing step call service

2019-03-04 10:44:01 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: domain=rest_command, service=engen_set, service_data=node=14, value=100>

2019-03-04 10:44:01 INFO (MainThread) [homeassistant.components.rest_command] Success call http://192.168.2.12:52125/alpha000/basic_set?node=14&value=100.

2019-03-04 10:44:01 INFO (MainThread) [homeassistant.helpers.script] Executing step call service

2019-03-04 10:44:01 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: domain=homeassistant, service=update_entity, service_data=entity_id=['sensor.living_room_left_status']>

2019-03-04 10:44:37 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state sensor.living_room_left_status=True; friendly_name=living_room_left_status @ 2019-03-04T10:44:37.311718-08:00>, entity_id=sensor.living_room_left_status, old_state=<state sensor.living_room_left_status=False;friendly_name=living_room_left_status @ 2019-03-04T10:43:35.498523-08:00>>

2019-03-04 10:44:37 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state switch.living_room_left=on; friendly_name=Living Room - Left @ 2019-03-04T10:44:37.351420-08:00>, entity_id=switch.living_room_left, old_state=<state switch.living_room_left=off; friendly_name=Living Room - Left @ 2019-03-04T10:43:35.195991-08:00>>

2019-03-04 10:44:37 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state group.all_switches=on; hidden=True, entity_id=('switch.foyer', 'switch.front_porch', 'switch.living_room_left', 'switch.living_room_right'), friendly_name=all switches, order=5, auto=True @ 2019-03-04T10:44:37.358485-08:00>,entity_id=group.all_switches, old_state=<state group.all_switches=off; hidden=True, entity_id=('switch.foyer', 'switch.front_porch', 'switch.living_room_left', 'switch.living_room_right'), friendly_name=all switches, order=5, auto=True @ 2019-03-04T10:43:35.292414-08:00>>

I will say that my logs are jammed packed with harmony errors, and a quick search on that shows that it’s a known error that might be fixed in 0.85. So I’m ignoring a lot of those errors right now.

I changed my log level for homeassistant.helpers.entity to critical so that I wouldn’t see all the harmony errors and that allowed me to get another example that might be cleaner to read. I’ve also given up on trying to renaming some things in what I’ve configured, engen_set is the light_set service. Engen is the name of the service, I was trying to simplify it above by calling it light_set.

When I click the switch:

2019-03-04 10:57:20 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: service=turn_on, service_data=entity_id=switch.living_room_left, domain=switch>
2019-03-04 10:57:20 INFO (MainThread) [homeassistant.helpers.script] Running script
2019-03-04 10:57:20 INFO (MainThread) [homeassistant.helpers.script] Executing step call service
2019-03-04 10:57:20 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: service=engen_set, service_data=node=14, value=100, domain=rest_command>
2019-03-04 10:57:20 INFO (MainThread) [homeassistant.components.rest_command] Success call http://192.168.2.12:52125/alpha000/basic_set?node=14&value=100.
2019-03-04 10:57:20 INFO (MainThread) [homeassistant.helpers.script] Executing step call service
2019-03-04 10:57:20 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: service=update_entity, service_data=entity_id=['sensor.living_room_left_status'], domain=homeassistant>

Then I waited until I saw the GUI update and opened the log to get the last few lines:

2019-03-04 10:57:34 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state sensor.living_room_right_status=False; friendly_name=living_room_right_status @ 2019-03-04T10:57:34.177487-08:00>, entity_id=sensor.living_room_right_status, old_state=<state sensor.living_room_right_status=False; friendly_name=living_room_right_status @ 20$
2019-03-04 10:57:34 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state sensor.living_room_left_status=True; friendly_name=living_room_left_status @ 2019-03-04T10:57:34.258167-08:00>, entity_id=sensor.living_room_left_status, old_state=<state sensor.living_room_left_status=False; friendly_name=living_room_left_status @ 2019-03-$
2019-03-04 10:57:34 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state switch.living_room_left=on; friendly_name=Living Room - Left @ 2019-03-04T10:57:34.265115-08:00>, entity_id=switch.living_room_left, old_state=<state switch.living_room_left=off; friendly_name=Living Room - Left @ 2019-03-04T10:56:01.177231-08:00>>
2019-03-04 10:57:34 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: new_state=<state group.all_switches=on; auto=True, hidden=True, order=5, entity_id=('switch.foyer', 'switch.front_porch', 'switch.living_room_left', 'switch.living_room_right'), friendly_name=all switches @ 2019-03-04T10:57:34.285926-08:00>, entity_id=group.all_switches, ol$

So it looks like you need the delay in the turn_on and turn_off actions. The question is how long to make it. Is 1 sec enough?

Also, is the device a switch or a dimmer? I’m seeing values like 100. Does that imply 100%? If it’s really a dimmer then you should probably be using a template light, not a template switch. But that’s really a separate issue than what we’ve been talking about so far.

Ah, I see. I hadn’t really thought of the delay before, but now I can tell when I fire it from HA if I have the api open in another window and hit refresh, it does take a few seconds to change. That sucks, but I get it… I guess I was just assuming because I saw it as changed in the body data that the api would have been changed at that point in time as well.

In reality, is it possible to update the status of the sensor with the body response, and if that’s wrong, the interval will correct it? Maybe that’s a horrible idea, but I don’t like that this toggle doesn’t feel like a switch for these lights.

It is a switch, not a dimmer. I think I did 100 because I have other dimmers that I was playing with at the time. A value of 1 or 100 changes the status to on.

It could be the response to the POST is just acknowledging what was requested, not that the state has actually changed. Don’t really know; just guessing.

The status of the template switch in HA is changed when it sees that the sensor’s state has changed. And that changes (by default) only periodically (based on its scan_interval setting, which by default is every 30 sec.) This is independent of the template switch changing the actual physical switch.

So, that’s why I suggested adding the homeassistant.update_entity service call in the template switch’s turn_on and turn_off actions. It will force the sensor to update itself. But if that happens too soon after sending the command to the physical switch, the sensor update may still see the old state. That’s why you might need a delay. If you need a delay, and how long it should be, depends on how quickly the physical switch will respond with its new state when queried by the sensor. You’ll just have to experiment to see what to use.

Regarding the periodic updating of the sensor, like I said, the default is every 30 seconds. This is needed to see when the physical switch is changed by some other means than through the HA switch template; e.g., when you physically toggle the switch, or use the external API, or whatever. You can change the scan_interval setting if you want HA’s sensor to update more quickly. E.g., you can add this to your living_room_left_status sensor:

    scan_interval: 5

This will cause it to update every 5 seconds instead of every 30 seconds.

I faced a similar situation. Solved it by inserting asyncio.sleep(3) in file https://github.com/home-assistant/core/blob/95dd9def665d85234aef28c1cb131e1ebd04f28c/homeassistant/components/rest/switch.py before await self.get_device_state(self.hass).

I have the core install on Raspberry Pi so the file switch.py was under /srv/homeassistant/lib/python3.9/site-packages/homeassistant/components/rest folder. The Home Assistant service must be restarted after editing this file.

Haven’t even gotten that far yet in Rest Switch as HA tells me

Invalid config for [rest]: [switch] is an invalid option for [rest]. Check: rest->rest->1->switch. (See /config/configuration.yaml, line 45). 

and my code is:

rest:
- resource: http://192.168.12.11/state.xml
    scan_interval: 32
    sensor:
      - name: Master Bedroom Chimney Fan
        value_template: "{{ value_json['datavalues']['relay1state'] }}"
     switch:
      - name: "Master Bedroom Chimney Fan"
        value_template: "{{ value_json['datavalues']['relay1state'] }}"
        turn_on:
          - service: rest_command.set_relay1state
            data:
              relay1state: 1
          - service: homeassistant.update_entity
            target:
              entity_id: sensor.master_bedroom_chimney_fan
          - delay: 00:00:15
          - service: homeassistant.update_entity
            target:
              entity_id: sensor.master_bedroom_chimney_fan
        turn_off:
          - service: rest_command.set_relay1state
            data:
              relay1state: 0
          - service: homeassistant.update_entity
            target:
              entity_id: sensor.master_bedroom_chimney_fan
          - delay: 00:00:15
          - service: homeassistant.update_entity
            target:
              entity_id: sensor.master_bedroom_chimney_fan

rest_command:
  set_relay1state:
    url: http://192.168.12.11/state.xml?relay1={{ relay1state }}