Component rest_command, add header information

hi, I’m currently trying to post data to mindergas.nl using rest_command. I want to call this service (service: rest_command.post_meterstanden in an automation, to post gas usage at a certain time.

- alias: 'Stuur gas meterstand door naar mindergas.nl'
  trigger:
    - platform: time
      hours: 0
      minutes: 10
      seconds: 00
  action:
      service: rest_command.post_meterstanden

When I read the documentation for the Rest_command component, I noticed there is no support for header. For the Rest_binary_sensor there is header support. Is it possible to include support for headers? This is because I need to post an authentication token in the header.

my rest_command in configuration.yaml:

    rest_command:
      post_meterstanden:
        method: POST
        url: 'https://www.mindergas.nl/api/gas_meter_readings'
        content_type: application/json
    #    headers:
    #      AUTH-TOKEN: !secret_token
    #      content-type: application/json
        payload: '{"date" : "{{states.sensor.date.state}}","reading" : {{ states.sensor.gas_consumption.state }}}'

Ok, got it to work:

config

rest_command:
  post_meterstanden:
    method: POST
    url: 'https://www.mindergas.nl/api/gas_meter_readings'
    headers:
      content-type: application/json
      AUTH-TOKEN: !secret: token
    payload: '{"date" : "{{states.sensor.yesterday.state}}","reading" : {{ states.sensor.gas_consumption.state }}}'

sensor

- platform: template
  sensors:
    yesterday:
      friendly_name: "yesterday"
      value_template: "{{ (as_timestamp(now()) - (24*3600)) | timestamp_custom('%Y-%m-%d', True) }}"

custom rest_command.py script

"""
Exposes regular rest commands as services.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/rest_command/
"""
import asyncio
import logging

import aiohttp
from aiohttp import hdrs
import async_timeout
import voluptuous as vol

from homeassistant.const import (
    CONF_TIMEOUT, CONF_USERNAME, CONF_PASSWORD, CONF_URL, CONF_PAYLOAD,
    CONF_METHOD,CONF_HEADERS)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv

DOMAIN = 'rest_command'

_LOGGER = logging.getLogger(__name__)

DEFAULT_TIMEOUT = 10
DEFAULT_METHOD = 'get'

SUPPORT_REST_METHODS = [
    'get',
    'post',
    'put',
    'delete',
]

COMMAND_SCHEMA = vol.Schema({
    vol.Required(CONF_URL): cv.template,
    vol.Optional(CONF_METHOD, default=DEFAULT_METHOD):
        vol.All(vol.Lower, vol.In(SUPPORT_REST_METHODS)),
    vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string,
    vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string,
    vol.Optional(CONF_PAYLOAD): cv.template,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): vol.Coerce(int),
    vol.Optional(CONF_HEADERS): {cv.string:cv.string},
})

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        cv.slug: COMMAND_SCHEMA,
    }),
}, extra=vol.ALLOW_EXTRA)


@asyncio.coroutine
def async_setup(hass, config):
    """Set up the REST command component."""
    websession = async_get_clientsession(hass)

    def async_register_rest_command(name, command_config):
        """Create service for rest command."""
        timeout = command_config[CONF_TIMEOUT]
        method = command_config[CONF_METHOD]

        template_url = command_config[CONF_URL]
        template_url.hass = hass

        auth = None
        if CONF_USERNAME in command_config:
            username = command_config[CONF_USERNAME]
            password = command_config.get(CONF_PASSWORD, '')
            auth = aiohttp.BasicAuth(username, password=password)

        template_payload = None
        if CONF_PAYLOAD in command_config:
            template_payload = command_config[CONF_PAYLOAD]
            template_payload.hass = hass

        headers = None
        if CONF_HEADERS in command_config:
            headers = command_config[CONF_HEADERS]

        @asyncio.coroutine
        def async_service_handler(service):
            """Execute a shell command service."""
            payload = None
            if template_payload:
                payload = bytes(
                    template_payload.async_render(variables=service.data),
                    'utf-8')

            try:
                with async_timeout.timeout(timeout, loop=hass.loop):
                    request = yield from getattr(websession, method)(
                        template_url.async_render(variables=service.data),
                        data=payload,
                        auth=auth,
                        headers=headers
                    )

                if request.status < 400:
                    _LOGGER.info("Success call %s.", request.url)
                else:
                    _LOGGER.warning(
                        "Error %d on call %s.", request.status, request.url)

            except asyncio.TimeoutError:
                _LOGGER.warning("Timeout call %s.", request.url)

            except aiohttp.ClientError:
                _LOGGER.error("Client error %s.", request.url)

        # register services
        hass.services.async_register(DOMAIN, name, async_service_handler)

    for command, command_config in config[DOMAIN].items():
        async_register_rest_command(command, command_config)

    return True

Hello @Yorick_Niezink ,

Pls looking via your custom component, is it safe to say that your component will allow for template payload while calling the service?

I need to be able to pass a template using variables when calling a service, not in the config file; as my data is based on a system’s response. I can notice a slight difference (as I am not a programmer) in the way you handle your payload.

Thanks

well the component is basically still the same one as https://home-assistant.io/components/rest_command/, but in this custom one you can set the headers.

When i read the documentation for the rest_command component, they say: The commands can be dynamic, using templates to insert values of other entities. Service call support variables for template stuff.

@Yorick_Niezink,

Thanks for the feedback and it’s the same thing I thought, but couldn’t get it to work.

Will try it again and see what I am getting wrong.

Kind regards

Yorick_Niezink,

Would like to use your script but do I have to make an additional sensor for my daily gas usage?
I use a Toon Thermostat so my smart meter is available.

Any advice?

Thanks in advance.

No you don’t have to, you can input your gas sensor directly into the payload.

payload: ‘{“date” : “{{states.sensor.yesterday.state}}”,“reading” : {{ states.sensor.gas_consumption.state }}}’

Thanks for your quick reply.

In my case I’ve got two sensors.

  • sensor.gas_today
  • sensor.gas_current

payload: ‘{“date” : “{{states.sensor.yesterday.state}}”,“reading” : {{ states.sensor.gas_today.state }}}’

This should be it i guess?

custom rest_command.py script does this run under Hass.io?

I added everything but during restarting I receive the following error:

2018-01-09 21:28:23 ERROR (MainThread) [homeassistant.config] Invalid config for [rest_command]: [headers] is an invalid option for [rest_command]. Check: rest_command->rest_command->post_meterstanden->headers. (See /config/configuration.yaml, line 186). Please check the docs at https://home-assistant.io/components/rest_command/

Looks like that headers is not allowed.

After using your example from 2 years ago, some fiddling around, and looking up examples. I finally got it to work. First of all, all configuration is always in configuration.yaml (yes, I am new to this, so I am slowly getting how this all works).

Then I had discover that the rest component now is fixed, so the current release of home assistant, just supports it.

The convention seems to be to replace !include token with the real API token you can fetch from mindergas. I had to play around with checking the configuration and finally just found that it worked.

Finally the automation has changed a little, so I have this configuration now:

   - id: mindergas
    alias: mindergas
    trigger:
      - platform: time
        at: '00:30:00'
    action:
      - service: rest_command.post_meterstanden

Again for newbie’s like me, the indenting does matter. It toke me a while… and time can also be simplified by using at, and then just do a time you like to trigger the automation. Also I had to add the id: but again it’s in the documentation.

So, thank you for leading the way. I got it to work in a couple of attempts.

So after hunting what was going on, the meterstanden did not update. I came to the logfile was right, the sensor.yesterday does not update by itself. It needs a trigger to update, so I added sensor.timeso the entity does update every minute. I tried sensor.date, and that did not work either. Then I figured out that you also need to implement the sensor.date_time to actually get state changes to sensor.date :wink:

My only question as noob, why is that not a default sensor?

My config looks like this now:

sensor:
  - platform: dsmr
    port: /dev/ttyUSB0
    dsmr_version: 4

  - platform: template
    sensors:
      yesterday:
        friendly_name: "yesterday"
        value_template: "{{ (as_timestamp(now()) - (24*3600)) | timestamp_custom('%Y-%m-%d', True) }}"
        entity_id: sensor.date

  - platform: time_date
    display_options:
      - 'time'
      - 'date'
      - 'date_time'
      - 'date_time_iso'
      - 'time_date'
      - 'time_utc'
      - 'beat'

hi @Yorick_Niezink , i am facing today also an issue with the rest sensor, i need an variablke header, is that still possible with your custom version? since its 2 years ago, it is still active/working? do you maybehave it on hacs/github? ?

thnx for the update

@pergola.fabio the official integration now supports this: RESTful Command - Home Assistant (home-assistant.io)

i am using rest sensor, but i need to send a variable token as a header
seems the headers dont support templating, thats my issue :frowning:

1 Like

Any solution on how to send header via template ?

1 Like