How to disable rest sensor when device has no power? (and reenable)

I use the REST API of my Sony Bravia TV to get it’s power state and pull other data from it.

The problem is, the TVs power is cut off when my master-slave socket sees that my AVR is turned off and when the TV has no power the API becomes unavailable.

But my rest sensors keep trying to pull data from the unavailable API resulting in the log to be spammed with errors over night which is pretty unnecessary, so I want to disable the sensors until the presence detection can ping my TV again. The other problem is that the media_player for the bravia won’t come up again as well.

Is it not possible to disable the sensors? I can’t find any solution to this…

Edit: Since there is no solution for this, I implemented an add-on to get around this issue, see here if you’re interested.

Set a really really long scan interval for your sensor.

Then when you want it to update use the service: homeassistant.update_entity

This can be automated. Use a time pattern trigger for whatever update interval you want with conditions to enable/disable it.

I’ve set the scan interval of all my rest sensors to 999999999 and did what you recommended, but there are still rest errors because it retries every 180 seconds after the first time the sensor fails to query the rest API:

20-04-10 21:34:02 ERROR (SyncWorker_4) [homeassistant.components.rest.sensor] Error fetching data: http://192.168.0.200/sony/videoScreen failed with HTTPConnectionPool(host='192.168.0.200', port=80): Max retries exceeded with url: /sony/videoScreen (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f73c19f0ed0>: Failed to establish a new connection: [Errno 113] Host is unreachable'))
2020-04-10 21:34:02 WARNING (MainThread) [homeassistant.components.sensor] Platform rest not ready yet. Retrying in 180 seconds.
2020-04-10 21:34:02 WARNING (MainThread) [homeassistant.components.sensor] Platform rest not ready yet. Retrying in 180 seconds.
2020-04-10 21:34:02 WARNING (MainThread) [homeassistant.components.sensor] Platform rest not ready yet. Retrying in 180 seconds.

So it doesn’t even solve the problem… Why isn’t it possible to just set the scan_interval to -1 to disable it? It’s kind of a bummer that something so simple isn’t possible since years…

Guess the only way I can solve this at the moment is by creating an API gateway that runs 24/7 which answers rest calls which can’t go through otherwise or use scripts instead of rest sensors.

This issue intrigued me so I created a RESTful Sensor, with a long scan_interval, and made it connect to a non-existent URL destination. I’m getting the same result you did.

Basically, if it can’t connect to the URL, it ignores the scan_interval and tries again according to its own interval. That interval increases (by 30 seconds) with each failed connection attempt (30, 60, 90, 120, 150, 180) until it reaches 180 and then seems to stick to using that interval.

Alright I finally implemented a proper workaround, it’s still a little rough around the edges and needs proper documentation but I implemented my own addon to get around this issue.

This basically hosts a small node server which can proxy API calls and provide failover responses if the target API isn’t reachable. It’s still work in progress, but it’s effectively working.

If anyone is interested to try it out, you can add my development addon repository.
If you want to wait for a beta or stable release you can simply add my beta or main repository

# development
https://github.com/shawly/hassio-addons-dev
# beta
https://github.com/shawly/hassio-addons-beta
# stable
https://github.com/shawly/hassio-addons

After installation you can open the web UI to get the IP or domain of the api gateway.

A new folder named api-scapegoat will be created in your config/ folder with some example files so you get a basic idea of how to configure it.

For example this is a config for my TVs API:

---
route: /sony
target: 'http://192.168.0.200'
failover:
  response:
    status_code: 202
    headers:
      content-type: application/json
      server: API Scapegoat
    body: '{ "error": [7, "not power-on"], "id": 40 }'

This basically means that every request to “<id>-api-scapegoat.local:3000/sony/" will be forwarded to "http://192.168.0.200/sony/”, and if 192.168.0.200 is offline, the failover response will be sent instead.

And here is a sensor I use to get the current picture setting from the TV:

############# Current Scene Sensor ##########
  - platform: rest
    name: Sony Bravia TV Current Scene
    resource_template: 'http://61b09487-api-scapegoat:3000/sony/videoScreen'
    headers:
      x-auth-psk: !secret bravia_psk
    method: POST
    payload: '{"method":"getSceneSetting","params":[""],"id":40,"version":"1.0"}'
    value_template:  >
        {%- if value_json.result[0].error is defined -%}
          error
        {%- else -%}
          {{- value_json.result[0].currentValue -}}
        {%- endif -%}

This way the sensor will make the request against the internal address of the container of the addon, you can also always expose the port via the configuration and add your homeassistant IP or domain if you prefer.

Now if the TV is online and reachable, the API will simply forward the request to the TV and return the response 1:1 without any changes.

If the power of the TV is cut off, the API will instead return:

{ "error": [7, "not power-on"], "id": 40 }

With an http status code of 202, so the rest sensor will not interpret it as a failed request.

Et voilà, your home assistant log will not be spammed anymore.

I’m planning to experiment a little more with this addon, technically it’s much more powerful since you could transform requests of APIs as well.

1 Like

Actually you can disable a rest sensor (sort of) when the target goes offline by using a resource template to redirect the sensor to a web page that you know will always be available. In the example below I use a binary sensor reflecting the state of the target:

 - platform: rest
    resource_template: >-
      {% if is_state('binary_sensor.philips_tv_status','on') %}
        http://192.168.2.65:1925/1/sources/current
      {% else %}
        https://ha-instance:8123/local/unavailable.html
      {% endif %}
    name: Philips TV Current Source
    value_template: '{{ value_json.id }}'
5 Likes

@FransO this is great idea, but what if the resource requires a POST method? In this case it won’t be able to get the unavailable.html as it would require GET method. Or is there a way to get the same file with POST?
Edit:
I’ve found a solution / hack to enable static content to be served by NGINX:

@shawly
I’m trying to use your API Scapegoat but i need a little help:

My original rest:

  - platform: rest
    name: CeBios now playing
    authentication: basic
    username: "xxxx"
    password: "xxxx"  
    resource: http://192.168.1.100:8080/jsonrpc
    params:
      request: '{"jsonrpc": "2.0", "id": 1, "method": "Player.GetItem", "params": {"properties": ["file"], "playerid": 1}}'
    value_template: '{{ value_json.result.item.file }}'

My (edited) example2.yaml

---
route: /api
target: 'http://192.168.1.100'
failover:
  response:
    status_code: 202
    headers:
      content-type: application/json
      server: API Scapegoat
    body: '{ "error": [7, "not power-on"], "id": 40 }'

My new rest:

  - platform: rest
    name: CeBios now playing
    authentication: basic
    username: "xxxx"
    password: "xxxx"  
    resource: http://7321f5f1-api-scapegoat:8080/jsonrpc
    params:
      request: '{"jsonrpc": "2.0", "id": 1, "method": "Player.GetItem", "params": {"properties": ["file"], "playerid": 1}}'
    value_template: '{{ value_json.result.item.file }}'

What am I doing wrong?

Route is still set to /api but you’d need /jsonrpc.

1 Like

Thanks, tried but still not working unfortunately:

---
route: /jsonrpc
target: 'http://192.168.1.100'
failover:
  response:
    status_code: 202
    headers:
      content-type: application/json
      server: API Scapegoat
    body: '{ "error": [7, "not power-on"], "id": 40 }'
  - platform: rest
    name: CeBios now playing
    resource: http://7321f5f1-api-scapegoat:8080/jsonrpc
    params:
      request: '{"jsonrpc": "2.0", "id": 1, "method": "Player.GetItem", "params": {"properties": ["file"], "playerid": 1}}'
    value_template: '{{ value_json.result.item.file }}'
[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 00-banner.sh: executing... 
-----------------------------------------------------------
 Add-on: API Scapegoat
 API gateway with failover for REST sensors.
-----------------------------------------------------------
 Add-on version: 1.0.0
 You are running the latest version of this add-on.
 System: Home Assistant OS 7.0  (aarch64 / odroid-n2)
 Home Assistant Core: 2021.12.3
 Home Assistant Supervisor: 2021.12.2
-----------------------------------------------------------
 Please, share the above information when looking for help
 or support in, e.g., GitHub, forums or the Discord chat.
-----------------------------------------------------------
[cont-init.d] 00-banner.sh: exited 0.
[cont-init.d] 01-log-level.sh: executing... 
Log level is set to INFO
[cont-init.d] 01-log-level.sh: exited 0.
[cont-init.d] api-scapegoat.sh: executing... 
[cont-init.d] api-scapegoat.sh: exited 0.
[cont-init.d] nginx.sh: executing... 
[cont-init.d] nginx.sh: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.
[12:52:15] INFO: Run API Scapegoat..
info: [API Scapegoat] Reading scapegoats from /config/api-scapegoat/
info: [API Scapegoat] Importing cebios.yaml...
info: [API Scapegoat] Creating route "/jsonrpc" for target "http://192.168.1.100"
info: [HPM] Proxy created: /  -> http://192.168.1.100
[12:52:16] INFO: Starting NGinx...

With the above configuration the “CeBios now playing” sensor is not created during a reboot of HASS.

To be sure I removed the user name and password from Kodi, but that wasn’t the issue.