core/homeassistant/components/shell_command.py

68 lines
1.9 KiB
Python

"""
Exposes regular shell commands as services.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/shell_command/
"""
import logging
import subprocess
import shlex
import voluptuous as vol
from homeassistant.helpers import template
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
DOMAIN = 'shell_command'
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
cv.slug: cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
def setup(hass, config):
"""Setup the shell_command component."""
conf = config.get(DOMAIN, {})
def service_handler(call):
"""Execute a shell command service."""
cmd = conf[call.service]
cmd, shell = _parse_command(hass, cmd, call.data)
if cmd is None:
return
try:
subprocess.call(cmd, shell=shell,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except subprocess.SubprocessError:
_LOGGER.exception('Error running command: %s', cmd)
for name in conf.keys():
hass.services.register(DOMAIN, name, service_handler)
return True
def _parse_command(hass, cmd, variables):
"""Parse command and fill in any template arguments if necessary."""
cmds = cmd.split()
prog = cmds[0]
args = ' '.join(cmds[1:])
try:
rendered_args = template.render(hass, args, variables=variables)
except TemplateError as ex:
_LOGGER.exception('Error rendering command template: %s', ex)
return None, None
if rendered_args == args:
# no template used. default behavior
shell = True
else:
# template used. Must break into list and use shell=False for security
cmd = [prog] + shlex.split(rendered_args)
shell = False
return cmd, shell