I want to be able to remotely trigger a webhook, and return data to the caller from the called shell_command.
I’ve got a webhook automation which uses curl through a shell_command to trigger an external API server which does stuff to a variety of things. The API server takes between 3-5 seconds to return the results of the stuff it does, and depending on the stuff it does the results are either JSON or plaintext. I have full control over the API server, and can easily wrap any of the plaintext into a JSON body for parsing, or really any other format, if required.
The automation is called either through a script which exposed to Google Assistant as a scene, or directly from a combination of Linux and Windows servers, networking devices, and some other stuff that isn’t integrated into Home Assistant.
I don’t see a way to get Home Assistant to capture the output from the shell_command. Is there a way to set that output to an input_string, or some other entity which could then be returned in the webhook response body?
If that’s not possible, how can I return whatever the shell_command spits out to whatever called the webhook? Specifically for Google Assistant, I need to be able to return the unique ID of the interaction that’s sent in the initial in order, I’ve got this covered with a customized version of the default webhook component.
As is, when Google Assistant triggers the webhook, everything works; the stuff that I want to get done gets done to the things, but Google Assistant just silently exits. If I can capture the data I can get it to say “stuff was done to thing” or “thing now stuffed.” I’d also like to be able to return arbitrary data to some of the other devices that use the automation, all of which are returned from the API server.
When you receive result in shell command, send it back to ha via another curl call to rest api.
You can change value of input_text, sensor… You can even create new sensor.
You’ll need to do this where the shell command is defined in HA. Personally I would make a bash (EDIT: or Python, thanks Igor) script doing all of this and just invoke that in HA.
But I came here to mention another way: You can also write the output to a file and put a file sensor on top of it.
If we can see your script we can probably help more.
#!/usr/bin/env python3
# Usage:
# tag_name_by_id tag_json_filename tag_id
import json
import sys
import requests
cfg = {}
cfg['url'] = 'https://my_homeassistant.local:8123'
cfg['token'] = 'my_secret_premanent_token'
def get_data(fname = 'tag'):
data = []
try:
# Opening JSON file
f = open(fname)
# returns JSON object as
# a dictionary
data = json.load(f)
except:
pass
finally:
try:
# Closing file
f.close()
except:
pass
return data
def find_name(data = [], id = ''):
name = ''
if len(data) > 0:
try:
for d in data['data']['items']:
if d['tag_id'] == id:
name = d['name']
except:
pass
return name
status = 255
if (len(sys.argv)-1) > 1:
data = get_data(str(sys.argv[1]))
if len(data) > 0:
name = find_name(data, str(sys.argv[2]))
if len(name) > 0:
target = "tag_{}_name".format(str(sys.argv[2])).replace("-", "_" )
url = "{}/api/states/sensor.{}".format(cfg['url'], target)
headers = {
"Authorization": "Bearer {}".format(cfg['token']),
"content-type": "application/json"
}
postdata = '{{ "state": "{}" }}'.format(name).encode("utf-8")
requests.urllib3.disable_warnings()
response = requests.post(url, headers=headers, data=postdata, verify=False)
if (response.status_code == 200) or (response.status_code == 201):
status = 0
else:
status = 1
else:
status = 2
else:
status = 3
else:
status = 4
sys.exit(status)
Ofcourse, your script can be written in bash, or anything else, the principle stays the same.
Execute something, catch result, send it back to HA via HTTP(S).
Best regards!
I think it’s weird that output from a shell command via shell_command can’t be explicitly and directly returned to the script that called it. I like your idea of writing to a file, which seems to me perhaps the easiest most direct way. I’ll give it try.
Shell scripts don’t return anything, except a status code about their execution (you can see this e.g. with echo $?, where e.g. 0 means success). I think you’re thinking of the output, which could be directed to stdout or errout, but that’s not the same as a return value. HA service don’t return anything (no services do, so it’s not unique to shell scripts).
Well, I did say output from the shell command, whether stdout or stderr. I should have said returned to whatever invoked it. According to the documentation, stdout and stderr are caught, but it isn’t clear to me how to access them. Shell Command - Home Assistant
This bit has promise, if I can figure it out: " With a 0 exit code, the output (stdout) of the command is used as value."
I see that too, and that question has been asked before without answer. I think it’s a copy paste from the command line sensor docs (clarification: I’m guessing that the one’s docs got copied from the other).
I think you’re correct about the info lingering from the Command line Sensor doc. I’ve put in a pull request for a proposed edit to take it out. After many years of looking at various things at github, this is the first project I’ve actually been motivated enough to create a github account. It’s my first pull request. I hope it goes okay.
And you might be right about using the Command line Sensor. I’ll check into that as well when I really have a need for this kind of thing, but I do like the idea of writing to a file, especially in the case of more complicated output.
Thanks for everyone’s help on this! A combination of both Igor and Pieter’s approaches got most of this working.
I was able to get this working on my dev server by running a python script invoked by a python_script which sets the output to a file, and a customized version of the webhook component that return’s that file content along with the response. However, getting it working on my real HomeAssistant setup is taking a bit more effort than it took in dev, unsurprisingly. The content I get back in Postman looks like what it should look like, but Google Assistant doesn’t like the response for a handful of reasons that I’m still debugging.
I don’t really want to create an override for the default webhook component, but it looks like I’m going to have to do more than a simple search and replace to get the custom version working alongside the default. So, I’m sure I’ll be poking around here a bunch in the future.
For those who may read this thread/topic actually, HA provides now, a solution for that problem.
But most readers already know that
“…Shell commands provide a service response in a dictionary containing stdout , stderr , and returncode . These can be used in automations to act upon the command results using response_variable.
…” Service calls - Home Assistant