How can I trigger a Python script on a remote RPi with HASS.IO?

I know how to trigger a local python script with HASS.IO. But now I need to execute a Python script on a networked RPi. There are a number of solutions like Paramiko to do this in Python but since HASS.IO is closed I wonder what solution I could use under the HASS.IO umbrella. Is there a particular component like AppDaemon that could achieve that?

I was looking at this as well over the week-end and this is how I solved it. My setup is pretty similar to yours as I have 1 RPi running HASS.IO and a second RPi with a screen connected running TileBoard. I wanted the screen to turn on when motion was reported.

I wrote a python script running as a service on the second RPi listening to a MQTT topic and executing a bash script in my case when the payload matches.

On the HASS.IO RPi, I created an automation to publish a payload to the selected MQTT topic when motion is detected and voilà!

1 Like

Ok, so the good thing is that it is possible. The bad thing is that I have no idea how to do what you just described. Could you possibly publish your scripts, so that I can work my way into this solution? :slight_smile:

Sure thing @sapnho.

I’m at work now with no access to the scripts. I’ll revert later today.

1 Like

Ah, that would be great. I guess it starts by installing the Mosquitto broker component in the HASS.IO add-on store.

Long day at work! Sorry for the delay.

My setup is as follow:

On the RPI I want the python script to execute, I made a first script which listens to a specific MQTT topic (/home/dashboard/entrance) and evaluate the messages it receives. It uses the Paho library (pip install paho-mqtt). If the message received is “movement”, then it executes another script called anotherScript.py.

#!/usr/bin/env python3

import paho.mqtt.client as mqtt
import anotherScript   #That the script you want to execute without .py


def on_connect(client, userdata, flags, rc):
  print("Connected to MQTT broker")

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "movement":
        print("Movement detected - turning the Dashboard screen on for 30 seconds ")
        anotherScript.myFunction()

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed to topic : " + str(mid) +" with QoS" + str(granted_qos))

client = mqtt.Client()

client.username_pw_set( "<YourMQTTBrokerUsername>" , "<YourMQTTBrokerPassword>" )

client.connect( "<YourMQTTBrokerIP>", 1883, 60)

client.subscribe( "/home/dashboard/entrance" , qos=1)

client.on_connect = on_connect

client.on_message = on_message

client.loop_forever()

Here is the anotherScript.py

def myFunction ():
    print ( "Hello World!" )

myFunction()

From here you need to execute the main script and you can test from HASS.IO developer tools (yourHASSIOURL:8123/dev-mqtt) and publish the payload on the defined topic and the second script python should execute.

To integrate further with Home Assistant, I added an automation to publish an MQTT payload (movement in my case) to the pre-defined topic (/home/dashboard/entrance in my case).

- id: '1978423740934'
  alias: Turn Dashboard screen on when movement is detected
  initial_state: True
  trigger:
  - entity_id: sensor.aeotec_zw100_multisensor_6_burglar
    platform: state
    to: '8'
  action:
  - service: mqtt.publish
    data:
      topic: "/home/dashboard/entrance"
      payload: "movement"
4 Likes

Thanks very much, Christophe! Will carefully study your solution tomorrow.

What did you do to run the python script as a service on the second RPi listening to the MQTT topic?

You run it in the background by adding an ampersand at the end.

python3 nameOfYourScript.py &

If you want your script to run automatically whenever your RPi restarts, you can try one of these methods : https://www.dexterindustries.com/howto/run-a-program-on-your-raspberry-pi-at-startup/

Good luck and keep me posted!

1 Like

Good luck and keep me posted!

Will most certainly do… :slight_smile:

Like I said in another thread, simply use ssh, like

ssh user@hostname commandtorun

Hi Nick, thanks, will try that as well. The nice thing about experimenting with MQTT is that the other computer can send back information as well.
Do I need to install a component to be able to use ssh in HA?

Ok, back to MQTT. It’s really an interesting approach to use and may be quite useful for other automation things as well. So definitely worth spending time on learning it.

Now, despite reading a lot about MQTT I am still at a stage where I am walking through a room blindfolded and people tell me “hot” and “cold”…

I have actually two “anotherScript.py”, one that turns HDMI on and the other that turns it off. Here is what I came up with so far. Let me know if this is “cold” or “lukewarm”. :slight_smile:

So my main script client_frame.py looks like:

#!/usr/bin/env python3

import paho.mqtt.client as mqtt
import hdmi_on   #The script that turns HDMI on without .py
import hdmi_off  #The script that turns HDMI off without .py

def on_connect(client, userdata, flags, rc):
  print("Connected to MQTT broker")

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "turn_monitor_on":
        print("Turning the monitor on")
        hdmi_on.myFunction()

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "turn_monitor_off":
        print("Turning the monitor off")
        hdmi_off.myFunction()

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed to topic : " + str(mid) +" with QoS" + str(granted_qos))

client = mqtt.Client()

client.username_pw_set( "<MQTTBrokerUsername>" , "<MQTTBrokerPassword>" )

client.connect( "<MQTTBrokerIP>", 1883, 60)

client.subscribe( "frame/monitor" , qos=1)

client.on_connect = on_connect

client.on_message = on_message

client.loop_forever()

And my two other scripts: hdmi_on.py

#!/usr/bin/python3
# coding: utf8 #

import subprocess  # for command execution

def myfunction():

	CONTROL = "vcgencmd" # command to turn the screen on
	CONTROL_UNBLANK = [CONTROL, "display_power", "1"] # command to turn the screen on
	subprocess.call(CONTROL_UNBLANK) # command to turn the screen on

myfunction()

and hdmi_off.py

#!/usr/bin/python3
# coding: utf8 #

import subprocess  # for command execution

def myfunction():

	CONTROL = "vcgencmd" # command to turn the screen off
	CONTROL_BLANK = [CONTROL, "display_power", "0"] # command to turn the screen off
	subprocess.call(CONTROL_BLANK) # command to turn the screen off

myfunction()

Could this work at all or would it blow up my little Raspberry Pi? :-):roll_eyes:

This is “warm” Wolfgang :grinning:

Your approach should work although you don’t really need two separate hdmi scripts. You could combine the two in a single script and have two separate functions (1 for hdmi_on and 1 for hdmi_off) which you’d call YourCombinedScript.hdmi_on() and YourCombinedScript.hdmi_off().

If you launch your main script, you should be able to monitor that it connects to your MQTT broker and starts listening to the defined topic. Next step is to use the Dev Tool in Home Assistant and publish “turn_monitor_on” or “turn_monitor_off” to the “frame/monitor” topic and the job is done!

You may also want to add a print in your “hdmi_off.py” and “hdmi_on.py” scripts to monitor they are correctly called from the main script.

1 Like

Encouraging words, thanks! I will keep experimenting and will report on any progress (or failures…)!

Ok, I got the connection between client and broker working. :star_struck:

For everyone who is following this thread: I originally got this error:

ImportError: No module named paho.mqtt.client

although I had installed paho. But for some reason the interpreter defaults to version 2.7 in Linux. So the command needed to be:

pip3 install paho-mqtt python-etcd

This now works. (https://stackoverflow.com/questions/41480256/import-error-paho-mqtt-client-not-found)

Now, let’s see if I can get the rest to work!

sorry i didnt read this earlier.

to get to your first question back, yeah appdaemon could do this as easy as you would like.
way more easy as with mqtt, but in the near future mqtt is also inside appdaemon.

what would you need to do?
pip3 install appdaemon on your remote system.
and thats about it. (oke, you need to create and configure appdaemon.yaml to connect to HA) :wink:

from that moment on you can write apps and listen to any state change in HA.
you could listen to a motion detector or a temperature change, you can switch a switch or anything you could do when you got it installed on the hassio device

1 Like

Thanks Rene. Great to see that there are so many solutions to get things done. I will definitely look into appdaemon but for now, I will try to get it working in MQTT - which then may end up in appdaemon in the future!

1 Like

Your approach should work although you don’t really need two separate hdmi scripts. You could combine the two in a single script and have two separate functions (1 for hdmi_on and 1 for hdmi_off) which you’d call YourCombinedScript.hdmi_on() and YourCombinedScript.hdmi_off().

What is the syntax for this?

This one gives me an error in the main file.

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "turn_monitor_on":
        print("Calling script to turn monitor on...")
        hdmi_on.myFunction()

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "turn_monitor_off":
        print("Calling script to turn monitor off...")
        hdmi_off.myFunction()

And in the combined file?

def myfunction(hdmi_on):

	print ( "I turned the screen on!" )

myfunction()

def myfunction(hdmi_off):

	print ( "I turned the screen off!" )

myfunction()

Probably totally messed up… :roll_eyes:

why are you splitting files up anyway?
but a few things to notice:
you call myFunction() but you named it myfunction

and you use an arg inside myfunction and create it twice.

if you want it to be combined you need something like

import hdmi
def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    print("Calling script to turn monitor off...")
    hdmi.myFunction(msg.payload.decode())

and hdmi.py

def myFunction(setting):
  if setting == "turn_monitor_off":
    print ( "I turned the screen off!" )
  elif setting == "turn_monitor_on":
    print ( "I turned the screen on!" )
  else:
    print ( "I got an unexpected setting!" )

and to show you how it would look as AD app:

import appdaemon.plugins.hass.hassapi as hass
class hdmi(hass.Hass):

  def initialize(self):
    self.listen_state(self.hdmi,"input_boolean.hdmi")

  def hdmi((self, entity, attribute, old, new, kwargs):
  if new == "off":
    print ( "I turned the screen off!" )
  else:
    print ( "I turned the screen on!" )

when you use an input_boolean to turn the tv on or off, but it could be done with any entity.

1 Like

Thanks Rene. I got it! I put everything together in one file and it works!

Based on Christophe’s and your posts, I put this together:

#!/usr/bin/env python3

import paho.mqtt.client as mqtt
import subprocess

def hdmi_on():
    CONTROL = "vcgencmd" 
    CONTROL_BLANK = [CONTROL, "display_power", "1"] 
    subprocess.call(CONTROL_BLANK) 
    print ( "I turned the screen on!" )

def hdmi_off():
    CONTROL = "vcgencmd" 
    CONTROL_BLANK = [CONTROL, "display_power", "0"] 
    subprocess.call(CONTROL_BLANK) 
    print ( "I turned the screen off!" )

def on_connect(client, userdata, flags, rc):
  print("Connected to MQTT broker")

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "turn_monitor_on":
        print("Calling script to turn monitor on...")
        hdmi_on()
    elif msg.payload.decode() == "turn_monitor_off":
        print("Calling script to turn monitor off...")
        hdmi_off()

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed to topic : " + str(mid) +" with QoS" + str(granted_qos))

client = mqtt.Client()

client.username_pw_set( "pi" , "xxx" )

client.connect( "192.168.xxx.xx", 1883, 60)

client.subscribe( "frame/monitor" , qos=2)

client.on_connect = on_connect

client.on_message = on_message

client.loop_forever()
1 Like