Fetching a token every hour

Hello.
I want to run a rest command every hour to fetch an auth token and then I want to use that token in some other automations.
I can’t find any good way of doing this.
If this were a normal application I will just make a post request and store the result token in some temporary variable, but I don’t find any HASSIO way of doing this.
What I tried so far:

  • Set the basic auth (username and password) in secrets.yaml
  • Because you can’t access secrets from templates ( I don’t understand this limitation) create an input text to hold the secret values
  • Create a rest sensor that uses the input text values like this:
- platform: rest
  resource: http://192.168.0.121/cgi-bin/api.cgi?cmd=Login&token=null
  method: POST
  payload: '[{"userName":"{{ states.input_text.xxx_user.state }}","password":"{{ states.input_text.xxx_password.state }}"}]'  
  value_template: '{{ value_json.0.value }}'
  json_attributes:
    - value.Token.lease_time 
    - value.Token.name 

And that’s all for now. The rest sensor has been failing but I don’t know why because I don’t know what is the exact request it is doing. Is there any log where I can see the actual request? If I try a curl request with this same values I get a satisfactory response, which looks like this:

[
   {
      "cmd" : "Login",
      "code" : 0,
      "value" : {
         "Token" : {
            "leaseTime" : 3600,
            "name" : "the-actual-token-value"
         }
      }
   }
]

Do you know of any other better approach?
I would love to use AppDaemon, but it seems to be a silo where you can’t fetch values from.

Thanks in advance

2 Likes

Doing this is nodered is easy. You would have something new to learn (if you don’t use it already). So, a bit of a learning curve… But, eventually it will be some much easier than yaml. My 2 cents.
GV

Should be able to add scan_interval to the restfulSensor config

- platform: rest
  resource: http://192.168.0.121/cgi-bin/api.cgi?cmd=Login&token=null
  method: POST
  payload: '[{"userName":"{{ states.input_text.xxx_user.state }}","password":"{{ states.input_text.xxx_password.state }}"}]'  
  value_template: '{{ value_json.0.value }}'
  scan_interval: 3600
  json_attributes:
    - value.Token.lease_time 
    - value.Token.name

As for why it’s failing, value_json.0.value will give you the following:

I use that template tab all the time to debug templates. I just copied your entire response, set it as a variable “value_json”, then try parsing it.

If you want that token, do the following

value_template: ‘{{ value_json.0.value.Token.name }}’

This will now set the sensor state value to just be ‘the actual token value’

Thank you very much for the template tab trick. I was using it for stuff like that but didn’t know that you can play that much with it (pasting actual json land all that). I am almost sure that what you posted is the first thing I tried, but using square bra quest notation. What about the json_values list ? What would be the proper way of setting it?

For lists, you can do either value_json.<list_id>… or value_json[<list_id>]…

Grabbing some example from W3School JSON arrays

{% set value_json = 
{
  "name":"John",
  "age":30,
  "cars": [
    { "name":"Ford", "models":[ "Fiesta", "Focus", "Mustang" ] },
    { "name":"BMW", "models":[ "320", "X3", "X5" ] },
    { "name":"Fiat", "models":[ "500", "Panda" ] }
  ]
 }
%}

{{ value_json.cars.0.name}} would be “Ford”

{{ value_json.cars[0].name}} would also be “Ford”

{{ value_json.cars[2].models[0] }} would be “Panda” (3rd element in cars, 2nd element in models)

This could also be {{ value_json.cars.2.models.0 }}

Either way is fine.

You could change yours to value_json[0].value.Token.name if you wanted. It’s the way I normally do lists at least.

Again, thank you for your examples and detailed responses, very appreciated.
But on the sensor side, I still get unknown as final value, which is something I don’t understand. The response at that is an string.
In any case, I ended using appdaemon, it is not that isolated at the end and I can get rid of a bunch of intermediary sensors and input texts.

thanks anyway!

I don’t like node-red because I find it’s visual way of describing flows a mess to maintain.
Yes, it is beautiful to the eye for small automations and for some time, but when you need to diff changes, save them to a remote repository or even take an overview of what is setup how, it is a time-waste.

I ended using appdaemon, which allows me to use a python. I prefer javascript, but appdaemon is very well integrated and it is very easy to communicate with hassio. I’ll write how I did it, but you are free to write how you would do it using node-red if you want.

Regards

For those interested, this is what I ended doing.
I used a simple app-daemn service which takes the secrets as input parameters and outputs the token to a dummy sensor that I can later read.
The advantage of this is that I only need to define a new appdaemon app where I can actually use !secret password, so I can keep things clean and tidy.
Here are the actual files:

in apps.yaml:

get_token:
  module: get_token
  class: GetToken
  user: !secret reolink_user
  password: !secret reolink_password
  url: http://192.168.0.121/cgi-bin/api.cgi?cmd=Login&token=nul

the module named get_token.py:

import appdaemon.plugins.hass.hassapi as hass
import requests

class GetToken(hass.Hass):
    def initialize(self):
        self.run_every(self.fetch_token, self.datetime(), 3600)

    def fetch_token(self, kwargs):
        user = self.args["user"]
        password = self.args["password"]
        login_url = self.args["url"]
        params = [{"cmd":"Login","action":0,"param":{"User":{"userName": user,"password": password }}}]
        request = requests.post(url = login_url, json=params)
        token = request.json()[0]["value"]["Token"]["name"]
        self.log("fetch token result: {}".format(request.status_code))
        self.set_state("sensor.reolink_token",state=token)

Then I just use the result like this in a script:

"camera_to_hall":
  alias: Camera to hall
  sequence:
    - service: rest_command.reolink_ptz
      data:
        ptz_id: 5
      data_template:
        token: {{ states('sensor.reolink_token.state') }}

Thanks to everybody that tried to help me.

I would like to see how this is done in nodered

@silfa718 Here you go… This example is with Synology Surveillance station.
You first connect using user/passwd. In the JSON payload you get a token, that has to be used for the next action. Doing this is in “pure” yaml in Home Assistant is probably doable. Mush more difficult, I think!

[{"id":"7c43c3c7.7a0a5c","type":"inject","z":"80238d0f.ea9f2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":120,"wires":[["e84577e5.50c6e8"]]},{"id":"e84577e5.50c6e8","type":"http request","z":"80238d0f.ea9f2","name":"Login","method":"GET","ret":"txt","paytoqs":false,"url":"http://IP_SYNO:5000/webapi/auth.cgi?api=SYNO.API.Auth&method=Login&version=3&account=USER&passwd=PASSWORD&session=SurveillanceStation&format=sid","tls":"","persist":false,"proxy":"","authType":"","x":270,"y":120,"wires":[["22f6376e.be9ca8"]]},{"id":"22f6376e.be9ca8","type":"json","z":"80238d0f.ea9f2","name":"","property":"payload","action":"","pretty":true,"x":390,"y":120,"wires":[["cd401a10.193f88"]]},{"id":"cd401a10.193f88","type":"change","z":"80238d0f.ea9f2","name":"Save sid","rules":[{"t":"set","p":"sid","pt":"flow","to":"payload.data.sid","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":120,"wires":[["15880df5.6726e2"]]},{"id":"15880df5.6726e2","type":"template","z":"80238d0f.ea9f2","name":"Motion Disable","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"http://IP_SYNO:5000/webapi/entry.cgi?api=SYNO.SurveillanceStation.Camera.Event&method=MDParamSave&source=-1&camId=3&version=1&_sid={{flow.sid}}\n","output":"str","x":260,"y":180,"wires":[["c645343d.545a08"]]},{"id":"c645343d.545a08","type":"http request","z":"80238d0f.ea9f2","name":"API","method":"GET","ret":"txt","paytoqs":false,"url":"{{{payload}}}","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":180,"wires":[["3831e9d8.c141a6"]]},{"id":"3831e9d8.c141a6","type":"http request","z":"80238d0f.ea9f2","name":"Logout","method":"GET","ret":"txt","paytoqs":false,"url":"http://IP_SYNO:5000/webapi/auth.cgi?api=SYNO.API.Auth&method=Logout&version=3&session=SurveillanceStation&_sid={{flow.sid}}","tls":"","persist":false,"proxy":"","authType":"","x":540,"y":180,"wires":[[]]}]
1 Like

Thanks for posting it, it is always interesting to see alternative approaches. However I see several limitations on your approach that make it not fit m7 requirements :

  • it logins each time, where the token is usable for an hour, so this is a lot of wasted requests
  • it does not make use of stored secrets
  • it does not integrates with hassio at all, so I can not turn it on/of from my dashboard

I didn’t look in detail your solution, just the screenshot, so feel free to correct anything that I may misunderstood

All of this is just an example. In this particular case, this is something triggered once a day. So, I don’t bother keeping the token between two runs.

Correct. I have never used secrets from HA in nodered.

Once again, just a simple example. Replace the inject node with a trigger based on a switch in HA…

I was asked for an example. It should just be considered as this. I chose, on purpose, a simple one…

In any case, you do not want to use nodered :wink:

GV

Yes, I understand that it was just a simple example. I was asking about the limitations to understand if it can be done close to what I did with appdaemon or not.
Thanks for your answers, of course.
You’re right, I don’t like node red because I find it too cumbersome to have to go to a web page to be a able to edit my code, but I like to have options so your answer is very much appreciated

Just to show a cleaner way to do the same thing… I am trying to convince you that nodered is the way to go :wink:

Once a day, reload the credential.
Once per hour, refresh the token (called here the sid)
Then, when an event “armed_away” is received from HA, run the “Motion disable” flow.

Not being a developper, appdaemon is not really for me!

The good thing with HA, is whether you like writing code, do stuff in yaml or play with coloured boxes in nodered, you can achieve what you want!

GV

1 Like

Thanks for this, got mine working getting the token.

Thank you very much for putting the time to showcase a better nodered implementation. As I said, I see the appealing on nodered, it is visually very attractive. It is just that I prefer to have everything together and in the same form (configs, other app daemon apps, etc), and for me writing some lines of code is not a problem.
But, again, thank you very much for showing it.

Dear,
could you please post a node-red example for me for setting a ptz position directly to the url of the camera including getting the token?
I’d like to learn node-red but this is all brand new to me.

Have you tried something?
Have you looked at the documentation of the camera or the synology surveillance station?
Have you tried using the example I gave to do something more basic?
I don’t do PTZ with my camera. So, I don’t have an example like that. If you start looking at the manual (camera, synologye…) it will give you what needs to be done.

GV

Seems the fwesterlink custom integration gives you the token in one of the state variables. Don’t know how often it updates though…
So I’ll continue in yaml automation as there is still a lot to learn for me in node red.

(EDIT)

I managed to install App-daemon and copied the files from your sample.

What should go in the “rest_command.reolink_ptz”?

Also, I do not seem to see a sensor.reolink_token in my dev tools!

How does one do a App daemon?
If you have some time.

I am trying to make 4 buttons in HA to orient my Reolink PTZ camera RLC-423 to 4 different placements.
And ended up on this thread.

Thanks!