[Solved] Add os.system into Python_script (or other python modules)

Hi Guys/Gals,

Hurray for the first post.

Problem: python_scripts has no access to specific features in python3. YMMV, but here is a patch that I made to give me access to os.system() calls (and any other os module function)

Solution: Patch your own code! Out of bands patches, whoop whoop! (Devs will not like us!)

— python_script-mod.py 2018-01-09 18:27:47.226150531 -0600
+++ /srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/python_script.py > 2018-01-09 18:26:26.360677898 -0600
@@ -140,6 +140,7 @@
builtins = safe_builtins.copy()
builtins.update(utility_builtins)
builtins[‘datetime’] = datetime

  • builtins[‘os’] = os
    builtins[‘sorted’] = sorted
    builtins[‘time’] = TimeWrapper()
    builtins[‘dt_util’] = dt_util

Change the bullet , into a + sign, silly formatting.

Save above in a file called python_scripts-mod.py.

Apply patch using:

patch < python_scripts-mod.py

And that will patch the file located here: /srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/python_script.py
If patch doesn’t work, try moving it into the components directory under /srv/homeassistant/…/components/

And you’ll now have access to:

os.system(), and other functions that home_assistant python_scripts should really not have access to!

You should probably really use the curl method as indicated below, it doesn’t meet my needs, so it wont work for what I want to do.

Original post banter:

I have a few different components that are working when I call a python_script, the only problem is I seem to lack some functionality in the Python. I’ve got some experience with python3, however it seems that hass is limited in which libraries are available for use when calling python_scripts/. Imports don’t work (as highlighted in other topics), and functions don’t seem to work properly either.

I assume the documentation for python_scripts feature is sparse likely because not many people are looking to write their own python scripts to do home automation…

Has anyone found a way to call child scripts from a python_script service? In ~/.homeassistant/ python_script /fork_py . py
I want to use as a general purpose handler for services and different events while using home assistant. I want to automated things using dynamic IRSEND commands: the HA configuration would get too convoluted to be able to do this properly (especially with integration with Amazon Alexa, and other services).

os.system() doesn’t work, and system() doesn’t work. I’ve tried call() as well as run(), but none seem to work. Can someone point me in the right direction?

I’ve stumbled around the github home-assistant/home-assistant/blob/8267a21bfe74520c7c8e0a8e94114476106261d9/homeassistant/components/python_script.py
It seems to support os.* but I can’t call os.system() calls.

People have gotten things to work with curl commands:
shell_command: “curl -K asdklajsdlkjsdkfjsdfklj/blah . php?var1=test&var2=apples” but I would rather not use <?php ?> if at all possible (less exposed services)

Regards,
James

1 Like

2018-01-08 18:39:54 INFO (MainThread) [homeassistant.helpers.script] Script Intent Script ActivateSceneIntent: Running script
2018-01-08 18:39:54 INFO (MainThread) [homeassistant.helpers.script] Script Intent Script ActivateSceneIntent: Executing step call service
2018-01-08 18:39:54 INFO (MainThread) [homeassistant.core] Bus:Handling <Event call_service[L]: service_call_id=3051550320-46, service=fork_py, service_data=entity_id=scene.something, domain=python_script>
2018-01-08 18:39:54 INFO (Thread-6) [homeassistant.components.python_script] Executing fork_py . py: {‘entity_id’: ‘scene.something’}
2018-01-08 18:39:54 INFO (Thread-6) [homeassistant.components.python_script.fork_py . py] Script Called
2018-01-08 18:39:54 INFO (Thread-6) [homeassistant.components.python_script.fork_py . py] some python output pre_process: scene.something
2018-01-08 18:39:54 INFO (MainThread) [homeassistant.core] Bus:Handling <Event /dev/null[L]: wow=from a Python script!>
2018-01-08 18:39:54 ERROR (Thread-6) [homeassistant.components.python_script.fork_py . py] Error executing script: name ‘run’ is not defined
Traceback (most recent call last):
File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/python_script . py”, line 166, in execute
exec(compiled.code, restricted_globals, local)
File “fork_py . py”, line 21, in

NameError: name ‘os’ is not defined
Error executing script: name ‘os’ is not defined
Traceback (most recent call last):
File “/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/python_script . py”, line 166, in execute
exec(compiled.code, restricted_globals, local)
File “fork_py.py”, line 21, in
NameError: name ‘os’ is not defined

################################

def alexa_function():
logger.info(“Detected a possible Alexa request.”)
logger.info(data)
crazy_file = ‘/tmp/some_crazy_file . txt’
fp_crazy_file = open(crazy_file,‘w’)
fp_crazy_file.write(“Yahoo!”)
fp.close()

def ha_function():
logger.info(“Regular Homeassistant call”)
logger.info(data)
hass.bus.fire(script, { “Pop”: “goes the weasel.” })

logger.info(“Script Called”)
pre_process = data . get(“entity_id”, None)
logger.info("pre_process: "+str(pre_process))
if (pre_process is not None):
alexa_function
else:
ha_function

script = data.get(“script”, “/dev/null”)
parameters = data.get(“parameters”, “/dev/null”)
logger.info(“Calling Script: {}”.format(script)+“, with parameters:"”+str(parameters)+“"”)
hass.bus.fire(script, { “wow”: “from a Python script!” })
os.system(“echo ‘run worked’ >> /tmp/external_program . txt”)
logger.info("Interesting bit: "+str(data))

#########intent_script.yaml#############

ActivateSceneIntent:
action:
service: python_script.fork_py
data_template:
entity_id: scene.{{ Scene | replace(" ", “_”) }}
speech:
type: plain
text: !include alexa_confirm.yaml

######Configuration.yaml snippet############

python_script:
python3_fork: /home/homeassistant/.homeassistant/python_script/fork_py . py {{message}}

P.S. Please ignore the spacedotspace, silly URL parser thinks I’m trying to add urls to my post.

I’m about to try it, does it still work on recent versions or does it make more sense just to jump to appdaemon for this?

The patch probably won’t apply to new versions of Home Assistant; all you need to do is append os.system into the line as per the patch. I’m still ages behind the times in regards to updating HA.

Patch – removes a line; and the ++ just adds the os module to the list of allowed python modules within HA.

I’m sure there is a better way then my proposed solution: it’s a definite security risk for public facing instances: my HA is behind a firewall with time-based port-knocking and isn’t generally publically available.

I wrote scripts on other devices that I hacked: and I needed a way to ssh into those embedded Linux devices to execute commands, at the time there was no HA integration for some of them (UBNT mFi, robot vac, etc).

:slight_smile:

I’m really surprised at the lack of basic builtins such as type() or enumerate() being included. Makes it pretty hard to write much of anything. I can work around enumerate() but not type(). Maybe app daemon is better…?

Same here I don’t see the point of such limitations, to not have the builtin of py3 is a big limitation, no enumerate (!!) nor time (how do you add a delay in such a script without that?? EDIT: it seems time.sleep is supported!). It is a pity ad this python_script concept is so great to stop messing too much with this super verbose yaml :frowning: … will look at appdaemon, but not sure it is not a big gun just to write some basic scripts.

python scripts have several advantages over app daemon. Python scripts can be triggered from automations like any other script, can be stopped / canceled, added to lovelace… App daemon requires much more configuration to do the same things, and are not that tightly integrated into home assistant. It is a bit ridiculous the limitations they have, looks like they don’t want people being free from ugly, unreadable verbose yaml.