Multiple Rest Switches from Single Rest Call/Query

Posting here to check if this is possible and also to document what I finally figured out after a lot of trial/error, since I couldn’t find much as I worked on this.

I have 2 Cisco PoE switches I use for my home network. I want to create switches in HA so I can control the PoE on each interface. I was able to create SNMP switches but they don’t self update so, if I change the setting on the switch itself, HA gets out of sync. I know I could ‘make’ this work but seems like REST is the better option.

SNMP Switches for PoE

For reference/documentation, here’s what worked in HA (it does take some configuration on the switch for SNMP permissions) :

- Cisco IOS Commands:

  • Create a view allowing access to SNMP
    snmp-server view HomeAssistantView iso included

  • Create an ACL to restrict access to SNMP
    ip access-list standard 14
    remark Allow SNMP from HomeAssistant and HomeAssistant Host
    5 permit host 192.168.1.x log
    10 permit host 192.168.1.x log
    15 permit host 192.168.1.x log
    50 deny any log
    exit

  • Create a group and associate it to the view and ACL
    snmp-server group HomeAssistantGroup v3 auth read HomeAssistantView access 14
    snmp-server group HomeAssistantGroup v3 auth write HomeAssistantView access 14

  • Create a user and add it to the group
    snmp-server user HomeAssistantUser HomeAssistantGroup v3 auth sha priv aes 256

- Rest Config YAML (per port)

  - platform: snmp
    name: Cisco_Core1_Port01_PoE
    host: !secret snmp3_coreswitch1_ip
    version: "3"
    username: !secret snmp3_user
    auth_key: !secret snmp3_auth_key
    auth_protocol: !secret snmp3_auth_protocol
    priv_key: !secret snmp3_priv_key
    priv_protocol: !secret snmp3_priv_protocol
    baseoid: 1.3.6.1.2.1.105.1.1.1.3.1.1
    payload_on: 1
    payload_off: 2

- Secrets YAML

snmp3_coreswitch1_ip: 192.168.x.x
snmp3_user: HomeAssistantUser
snmp3_auth_key: <AuthPassword>
snmp3_auth_protocol: hmac-sha
snmp3_priv_key: <PrivPassword>
snmp3_priv_protocol: aes-cfb-256

Sorry, off topic, but didn’t want to start an entire new topic just to document that. Hopefully, it helps someone else in the future.

The actual point of this post…

Anyway, that worked but got out of sync so I figured RESTful switches made more sense.

So, I’m able to create a RESTful switch using the following (RESTCONF must be enabled on the switch and a Privilege 15 user credential must be specified below):

Note the last part of the URL, which is the interface - 1%2F0%2F1 = 1/0/1 . This must be updated on each line and match whatever Cisco reports the interface as.

  - platform: rest
    name: Cisco Core 1 - Port 01 - PoE
    unique_id: Cisco_Core1_Port01_PoE
    resource: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1%2F0%2F1
    verify_ssl: false
    method: patch
    scan_interval: 180
    timeout: 60
    username: !secret cisco_rest_user
    password: !secret cisco_rest_password
    headers:
      Content-Type: application/yang-data+json
      Accept: application/yang-data+json
    body_on: '{"Cisco-IOS-XE-native:GigabitEthernet":{"name":"1/0/1","Cisco-IOS-XE-power:power":{"inline":{"auto-choice":"","auto":""}}}}'
    body_off: '{"Cisco-IOS-XE-native:GigabitEthernet":{"name":"1/0/1","Cisco-IOS-XE-power:power":{"inline":{"never-choice":"","never":""}}}}'
    is_on_template: "{{ value_json['Cisco-IOS-XE-native:GigabitEthernet'][0]['Cisco-IOS-XE-power:power']['inline']['auto'] != NULL }}"

This works but, to monitor all 24 ports on both switches, I’m making 48 REST calls and it’s causing delays, which fills up my logs with 47 messages every 3 minutes.

2024-09-24T16:37:12.055051546Z 2024-09-24 12:37:12.054 WARNING (MainThread) [homeassistant.helpers.entity] Update of switch.cisco_core_2_port_11_poe is taking over 10 seconds
2024-09-24T16:37:12.055113717Z 2024-09-24 12:37:12.054 WARNING (MainThread) [homeassistant.helpers.entity] Update of switch.cisco_core_2_port_04_poe is taking over 10 seconds
2024-09-24T16:37:12.055189260Z 2024-09-24 12:37:12.054 WARNING (MainThread) [homeassistant.helpers.entity] Update of switch.cisco_core_2_port_19_poe is taking over 10 seconds

Finally, the question… :grimacing:

Is there any way to create multiple switches with a single REST query? I know I’d need to modify the query itself to remove the interface and then reference the items in the array for each switch, but that’s still better than 48 queries (I think).

I know you can do it for Sensors and Binary_Sensors in the main REST integration but Switch isn’t supported. Any idea if that’s on the roadmap or if there is a reason it just can’t work that way?

Is there some other way that I’m just too knuckleheaded to find with a search (or…a lot of searches, as the case may be)?

Any help is much appreciated. Thanks!

1 Like

Following…

No. So to overcome your issue do this:

Set up multiple state sensors with one rest call using the rest integration.

Simplistic example:

rest:
  - authentication: basic
    username: "admin"
    password: "password"
    scan_interval: 60
    resource: http://example.com
    sensor:
      - name: "Switch 1 State"
        value_template: "{{ value_json.switch_1 == 1 }}"
      - name: "Switch 2 State"
        value_template: "{{ value_json.switch_2 == 1 }}"
.
.
.

      - name: "Switch n State"
        value_template: "{{ value_json.switch_n == 1 }}"

Construct on and off rest commands for each switch (these are non-polling and will not affect your resource):

rest_command:
  switch_1_on:
    url: "http://example.com/on_1"
  switch_1_off:
    url: "http://example.com/off_1"
  switch_2_on:
    url: "http://example.com/on_2"
  switch_2_off:
    url: "http://example.com/off_2"
.
.
.
  switch_n_on:
    url: "http://example.com/on_n"
  switch_n_off:
    url: "http://example.com/off_n"

Use the template switch integration to pull these elements together.

You may find your switch states bounce due to the state sensor polling interval so use the homeassstant update entity service to update your rest state sensor whenever a switch command is sent. e.g.

switch:
  - platform: template
    switches:
      switch_1:
        value_template: "{{ is_state('sensor.switch_1_state, 'on') }}"
        turn_on:
          - action: rest_command.switch_1_on
          - action: homeassistant.update_entity
            entity_id: sensor.switch_1_state
        turn_off:
          - action: rest_command.switch_1_off
          - action: homeassistant.update_entity
            entity_id: sensor.switch_1_state

      switch_2:
        value_template: "{{ is_state('sensor.switch_2_state, 'on') }}"
        turn_on:
          - action: rest_command.switch_2_on
          - action: homeassistant.update_entity
            entity_id: sensor.switch_2_state
        turn_off:
          - action: rest_command.switch_2_off
          - action: homeassistant.update_entity
            entity_id: sensor.switch_2_state
.
.
.
      switch_n:
        value_template: "{{ is_state('sensor.switch_n_state, 'on') }}"
        turn_on:
          - action: rest_command.switch_n_on
          - action: homeassistant.update_entity
            entity_id: sensor.switch_n_state
        turn_off:
          - action: rest_command.switch_n_off
          - action: homeassistant.update_entity
            entity_id: sensor.switch_n_state

As long as you are not changing multiple switches at a time this should not overload your resource.

Thank you. Appreciate the reply and the detail. I was able to make that work, just as you described. It’s better, as it’s not filling my logs with errors. It does seem to take about 12 seconds for the REST query to run, so I still get a single error but, from what I’ve read, the 10 second timeout ‘error’ isn’t configurable so I guess it’ll be what it’ll be.

Any idea if supporting the switch entity under the main REST call directly is on the roadmap somewhere? This is so much more work and results in nearly 200 entries and 96 entities vs 48 when using the RESTful Switch. I mean, I’m glad the functionality is there, but it seems overly complicated if there’s a way to support switches under REST.

Here’s my working config, for future searchers to find…:

  • Configuration.yaml (I break out the config to multiple files)
rest: !include CustomConfigs/Rest.yaml
rest_command: !include CustomConfigs/RestCommand.yaml
switch: !include CustomConfigs/Switches.yaml
  • Rest.yaml
# Sensors
- resource: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet
  verify_ssl: false
  authentication: basic
  username: !secret cisco_rest_user
  password: !secret cisco_rest_password
  headers:
    Content-Type: application/yang-data+json
    Accept: application/yang-data+json
  scan_interval: 300
  sensor:
    - name: PoE - Cisco Core 1 - Interface 01 - State
      unique_id: poe_cisco_core_1_interface_01_state
      force_update: true
      value_template: "{{ value_json['Cisco-IOS-XE-native:GigabitEthernet'][1]['Cisco-IOS-XE-power:power']['inline']['auto'] != NULL }}"
      json_attributes_path: "$['Cisco-IOS-XE-native:GigabitEthernet'][1]"
      json_attributes:
        - "name"
        - "description"
    - name: PoE - Cisco Core 1 - Interface 02 - State
      unique_id: poe_cisco_core_1_interface_02_state
      force_update: true
      value_template: "{{ value_json['Cisco-IOS-XE-native:GigabitEthernet'][12]['Cisco-IOS-XE-power:power']['inline']['auto'] != NULL }}"
      json_attributes_path: "$['Cisco-IOS-XE-native:GigabitEthernet'][12]"
      json_attributes:
        - "name"
        - "description"
  • RestCommands.yaml (Not the interface name in the URL and the payload. It must match what the Cisco switch uses)
# Commands
poe_cisco_core_1_interface_01_on:
  url: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1%2F0%2F1
  verify_ssl: false
  method: patch
  timeout: 15
  username: !secret cisco_rest_user
  password: !secret cisco_rest_password
  headers:
    Content-Type: application/yang-data+json
    Accept: application/yang-data+json
  payload: '{"Cisco-IOS-XE-native:GigabitEthernet":{"name":"1/0/1","Cisco-IOS-XE-power:power":{"inline":{"auto-choice":"","auto":""}}}}'

poe_cisco_core_1_interface_01_off:
  url: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1%2F0%2F1
  verify_ssl: false
  method: patch
  timeout: 15
  username: !secret cisco_rest_user
  password: !secret cisco_rest_password
  headers:
    Content-Type: application/yang-data+json
    Accept: application/yang-data+json
  payload: '{"Cisco-IOS-XE-native:GigabitEthernet":{"name":"1/0/1","Cisco-IOS-XE-power:power":{"inline":{"never-choice":"","never":""}}}}'

poe_cisco_core_1_interface_02_on:
  url: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1%2F0%2F2
  verify_ssl: false
  method: patch
  timeout: 15
  username: !secret cisco_rest_user
  password: !secret cisco_rest_password
  headers:
    Content-Type: application/yang-data+json
    Accept: application/yang-data+json
  payload: '{"Cisco-IOS-XE-native:GigabitEthernet":{"name":"1/0/2","Cisco-IOS-XE-power:power":{"inline":{"auto-choice":"","auto":""}}}}'

poe_cisco_core_1_interface_02_off:
  url: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet=1%2F0%2F2
  verify_ssl: false
  method: patch
  timeout: 15
  username: !secret cisco_rest_user
  password: !secret cisco_rest_password
  headers:
    Content-Type: application/yang-data+json
    Accept: application/yang-data+json
  payload: '{"Cisco-IOS-XE-native:GigabitEthernet":{"name":"1/0/2","Cisco-IOS-XE-power:power":{"inline":{"never-choice":"","never":""}}}}'


  • Switches.yaml
- platform: template
  switches:
    poe_cisco_core_1_interface_01:
      friendly_name: PoE - Cisco Core 1 - Interface 01
      unique_id: poe_cisco_core_1_interface_01
      value_template: "{{ is_state('sensor.poe_cisco_core_1_interface_01_state', 'True') }}"
      turn_on:
        - action: rest_command.poe_cisco_core_1_interface_01_on
        - action: homeassistant.update_entity
          entity_id: sensor.poe_cisco_core_1_interface_01_state
      turn_off:
        - action: rest_command.poe_cisco_core_1_interface_01_off
        - action: homeassistant.update_entity
          entity_id: sensor.poe_cisco_core_1_interface_01_state

- platform: template
  switches:
    poe_cisco_core_1_interface_02:
      friendly_name: PoE - Cisco Core 1 - Interface 02
      unique_id: poe_cisco_core_1_interface_02
      value_template: "{{ is_state('sensor.poe_cisco_core_1_interface_02_state', 'True') }}"
      turn_on:
        - action: rest_command.poe_cisco_core_1_interface_02_on
        - action: homeassistant.update_entity
          entity_id: sensor.poe_cisco_core_1_interface_02_state
      turn_off:
        - action: rest_command.poe_cisco_core_1_interface_02_off
        - action: homeassistant.update_entity
          entity_id: sensor.poe_cisco_core_1_interface_02_state

Even for 48 sensors that seems excessive. The single resource call should not take that long and processing the returned data shouldn’t either. Enabling debug logging might revel where this issue is.

Not that I know of. I’m not sure how it would work or even if it could, as the resource for each switch command would be different.

In your switch value templates you have:

Double check the state of the sensors in Developer Tools → States. Make sure the state is capitalised. I don’t think it will be.

Also if these only have the true/false state you could make them binary_sensors instead of sensors in the rest integration.

# Sensors
- resource: https://192.168.1.x/restconf/data/Cisco-IOS-XE-native:native/interface/GigabitEthernet
  verify_ssl: false
  authentication: basic
  username: !secret cisco_rest_user
  password: !secret cisco_rest_password
  headers:
    Content-Type: application/yang-data+json
    Accept: application/yang-data+json
  scan_interval: 300
  binary_sensor: # <-- HERE
    - name: PoE - Cisco Core 1 - Interface 01 - State etc...

This would mean changing your switch value templates to:
value_template: "{{ is_state('binary_sensor.poe_cisco_core_1_interface_01_state', 'on') }}"

Unfortunately, the delay is on the Cisco switch side. The JSON being retrieve comes back layers and layers deep and I guess the Cisco just isn’t optimized to return a big database call. Even running the query from PowerShell takes 8-10 seconds. That’s why I was originally doing the RESTful switch option instead, as I can target just the interface I need. Oh well, I think this is still the better alternative.

In this particular case, a single resource would work for multiple switches so it theoretically could be implemented under the REST integration directly, but that may not be the case for other REST calls, as in some of the sample data you posted earlier. Perhaps not worth the effort to implement.

Yup, it does return True with the capital. I actually originally had it checking for true and it was failing. The way the query returns is odd. Basically, it doesn’t return any value/data, it returns a tree branch that either exists or doesn’t exist if the power is on. The only way I got it to work was != NULL, which seems to be consistent so :crossed_fingers:.

Unless there is some performance oriented reason to switch to binary_sensor, I’m gonna let it ride as is, especially since those sensors are just background data anyway and I’m not sure if the ‘True’ value will convert to $true (or whever the true would be). At this point, it’s basically doing what I need and I’ve burned nearly 2 days getting it there so onward and upward!

Again, I really appreciate all your help and quick responses!

1 Like

Only just thought of this: I wonder if SNMP would be a more efficient approach

I originally used SNMP for the switches but, because there was no polling, they get out of sync. Unless you mean switching the state sensor over to SNMP and putting some sort of update_entity in an automation to poll it every so often. I didn’t try that. I’d have to look into how to get that state from the switch but it’s per interface so I’d still be looking at kicking off 48 individual calls every so often to get it.