RPi Screen Control via MQTT - Need a bit of advice

I have been trying to create a room control screen so that I can control the smart elements of each of my rooms.

The basis for the control screen is a raspberry pi with touchscreen that was donated to me.

To create the control screen as follows:

  1. I created a raspberry pi in kiosk mode based on these instructions: https://desertbot.io/blog/raspberry-pi-touchscreen-kiosk-setup

  2. KIOSK_URL can be to your Lovelace or in my case it is to my TileBoard - New dashboard for Homeassistant

  3. I created a new script and saved it to /home/pi/touch-screen-power.py. The script is as follows:

#!/usr/bin/python
import paho.mqtt.client as mqtt
import sys
import signal
import os
from subprocess import call
import time


def sigint_handler(signal, frame):
        print("Exiting")
        sys.exit(0)
signal.signal(signal.SIGINT, sigint_handler)

def monitor_on():
    print("Switch on")
    call('XAUTHORITY=~pi/.Xauthority DISPLAY=:0 xset dpms force on',shell=True)
    client.publish("touchscreen/state", "ON")

def monitor_off():
    print("Switch off")
    call('XAUTHORITY=~pi/.Xauthority DISPLAY=:0 xset dpms force off',shell=True)
    client.publish("touchscreen/state", "OFF")

def on_connect(client, userdata, flags, rc):
    print("Connected with result code "+str(rc))
    client.subscribe("touchscreen/cmd/#")

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.payload.decode("utf-8")))
    if msg.topic == 'touchscreen/cmd':
      if str(msg.payload.decode("utf-8")) == 'ON':
        print("Monitor On")
        monitor_on()
      if str(msg.payload.decode("utf-8")) == 'OFF':
        print("Monitor Off")
        monitor_off()

# Set up and connect to MQTT broker
client = mqtt.Client(client_id="<client_id>")
client.username_pw_set("<username>", password="<password>")
client.on_connect = on_connect
client.on_message = on_message
client.connect("<IP>", 1883, 60)
print("Running...")
client.publish("touchscreen/MQTTclient/state", "ON")
client.loop_start()

def status_on():
    print("Switch check on")
    client.publish("touchscreen/state", "ON")

def status_off():
    print("Switch check off")
    client.publish("touchscreen/state", "OFF")

while True:
    print("Switch check")
    p = subprocess.Popen('vcgencmd display_power', shell=True, stdout=subprocess.PIPE).communicate()[0]
    r=p.rstrip()
    check=r.decode("utf-8")
    if check == "display_power=0":
       status_off()
    if check == "display_power=1":
       status_on()


client.disconnect()
client.loop_stop()

  1. Started the Mosquitto broker Add-on on Home Assistant.

  2. Installed python and paho on the raspberry pi:

sudo apt-get install python3
pip3 install paho-mqtt
  1. Installed Mosquitto broker on the raspberry pi:
sudo apt-get install mosquitto clients
  1. Open openbox autostart:
sudo nano /etc/xdg/openbox/autostart
  1. Add the python script to openbox autostart by adding: python3 /home/pi/touch-screen-power.py above chromium-browser --noerrdialogs --disable-infobars --kiosk $KIOSK_URL

  2. I then added the following to my configuration.yaml:

switch:
  - platform: mqtt
    unique_id: touch_screen
    name: "Touch Screen Power"
    state_topic: "touchscreen/state"
    command_topic: "touchscreen/cmd"
    payload_on: "ON"
    payload_off: "OFF"
    state_on: "ON"
    state_off: "OFF"

All of this gives me a screen I can turn on and off from home assistant.

My questions, as this is the first time using MQTT and Python:

  1. My script seems to work but it was copy and pasted from a few other sources, are there any obvious flaws (we all have to start somewhere and I am still learning)?

  2. Do I need mosquitto clients on the pi if I have paho?

  3. Due to the while True: in the python code I get hundreds of MQTT messages. This seems useful as my switch is always in the correct state, even if I control it manually. Is this a problem? Will it reduce performance? etc.

Thanks all.

3 Likes

I ran into an issue when I when from python2 to 3 in that if I used the same parameters for paho mqtt it would not work in python3 pip version; the main issue I found was if the port is the default then you can’t put it in. I know what the documentation says, but it just won’t work if you put in the port.
When I get home I can post my code that I have for python3 for mqtt publishing.

I had the same problem but it worked by using:

pip3 install paho-mqtt

And not

pip install paho-mqtt

Hi, just out of curiosity: what is the reason/use case for wanting a switch to turn on/off the RPi display?

My MQTT() routine:

def MQTT(MQ_Topic,MQ_Payload):
  # Send to the MQTT Broker
  try:
    if MQTT_enable is True:
      if Debug2 is True:
        print ("MQTT pre Client")
      mqttc = mqtt.Client("python_pub")
      if Debug2 is True:
        print ("MQTT pre Connect")
      mqttc.connect(Broker_IP)
      if Debug2 is True:
        print ("MQTT pre Publish")
      mqttc.publish(MQ_Topic, MQ_Payload, retain = True)
      if Debug is True:
        print ("MQTT Publish Complete for " + MQ_Topic)
      if MQTT_enable is False:
        if Debug is True:
          print ("MQTT is not enabled")
  except:
    # Prevent crashing if Broker is disconnected
    if Debug is True:
      print ("MQTT Failed to publish All")

I like to add a debug option to all my scripts so I can easily turn on and off printing to screen.

Misc variables:

# MQTT Configuration:
MQTT_enable     = True          # Enable MQTT
Broker_IP = "10.74.1.224"       # MQTT Broker IP
Broker_Port = "1883"            # MQTT Broker Port

I don’t use the Port in this script since this one is Python3 but in my Python2 scripts I have to pass it. In Python3 I get errors if I try to pass I think becuase it is the default.

The simple command I use inside my script to call the routine in my script:

MQTT("docker/ContRun",Dock_MQTT)

The “docker/ContRun” is the MQTT topic.
The Dock_MQTT is just a variable I pass.
I found it easier to push everything to a variable then pass the variable to MQTT.

When the screen goes off it has an on screen display that pops up that says “screen powered off” then “power saving mode”. Another screen has “hdmi 1” when it comes on.

My long term plan would be to have one in every room I think it would look a bit “unprofessional” if we see these on screen messages when the screen switches on or off.

Therefore as each room is accessed via the hall I would switch all screens on when motion is detected in the hall and would switch off individual screens when there is no motion in each room for 5 mins.

E.g.
I come home -> all screens turn on -> I enter the Kitchen -> motion detected in the kitchen screen stays on and all other screens go off in other rooms after 5 mins -> enter Living room via hall -> all screens on when I am in the hall -> living room stays on all others off -> etc.

This way the screen is always on in the room I am in but I will never seen the panel switch on or off. It would be like they were always on.

To do this I needed a way to control the screens via home assistant.

I see…
I have one that runs raspbian and with Chromium in kiosk mode I created a html page with html tabs to switch between HA/Control for my Music System/Nextcloud Calendar/Detailed weather page.
For now I’m using a screensaver to blank the screen.

I have another one that is a dedicated Music Player and control for it.
This one has a big clock/date screensaver that changes it’s background between playing and paused.
I have searched for this option on Raspbian but have not come across it yet.

Your idea of turning off is interesting, will keep an eye on it.

Interesting, how does this work? I am currently using TileBoard - New dashboard for Homeassistant with pop up iframe tiles to do most of this.

I don’t think my html scripting would be good enough to make anything that looks nice. But it does sound like it might be more flexible than what I have now.

Do you have a screenshot?

This is another option for me but there are two things that I thought about it:

  1. How much electricity does it use? If I have one in every room and they are all on all of the time it starts to add up.

  2. There are some use cases where I want the back light to go off. Like when we are sleeping. I don’t think a screen saver would achieve this?

I’m not at the point that I can have everything in HA the way I want so that’s why I searched for another solution and more flexibility was the main reason for it
It’s mainly my music system: this has a webUI which is far more better then what can be done in HA.

My html coding skills are very basic too but it’s quite simple, Chromium starts with this html file:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {font-family: Arial;}

/* Style the tab */
.tab {
  overflow: hidden;
  background-color: #f1f1f1;
}

/* Style the buttons inside the tab */
.tab button {
  background-color: inherit;
  float: left;
  border: none;
  outline: none;
  cursor: pointer;
  padding: 4px 26px;
  transition: 0.3s;
  font-size: 17px;
}

/* Change background color of buttons on hover */
.tab button:hover {
  background-color: #ddd;
}

/* Create an active/current tablink class */
.tab button.active {
  background-color: #ccc;
}

/* Style the tab content */
.tabcontent {
  display: none;
  padding: 26px 12px;
  
  border-top: none;
}
</style>

<script type="text/javascript" src="jquery.mobile-1.4.5.min.js"></script>

</head>

<body>

<!-- Tab links -->
<div class="tab">
 <button class="tablinks" onclick="openPage(event, 'HomeAssistant')">Domotica</button>
 <button class="tablinks" onclick="openPage(event, 'Music')">Music</button>
 <button class="tablinks" onclick="openPage(event, 'Weather')">Weather</button>
 <button class="tablinks" onclick="openPage(event, 'pCP')">pCP</button>
 <button class="tablinks" onclick="openPage(event, 'Cal')">Cal</button>
 <button class="tablinks" onclick="openPage(event, 'HomeAssistant2')">Domotica 2</button>
 <button class="tablinks" onclick="openPage(event, 'pCP-RB')">pCP-RB</button>
</div>

<!-- Tab content -->
<div id="HomeAssistant" class="tabcontent">
  <iframe src="http://172.16.0.5:8123/lovelace/index.htm" style="position:fixed; top:40px; left:0; bottom:10px; right:0; width:100%; height:95%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</div>

<div id="Music" class="tabcontent">
  <iframe src="http://172.16.0.100:9000/material/index.htm" style="position:fixed; top:40px; left:0; bottom:10px; right:0; width:100%; height:93%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</div>

<div id="Weather" class="tabcontent">
  <iframe src="http://forecabox.foreca.com/get/45845" style="position:fixed; top:40px; width="300" height="250" marginwidth="0" marginheight="0" frameborder="0" scrolling="no" allowtransparency="true"></iframe>
</div>

<div id="pCP" class="tabcontent">
  <iframe src="http://172.16.0.22/cgi-bin/main.cgi" style="position:fixed; top:40px; left:0; bottom:10px; right:0; width:100%; height:93%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</div>

<div id="Cal" class="tabcontent">
  <iframe src="https://172.16.0.6/nextcloud/index.php/apps/calendar/p/7XGttiN3DFAWboRt/dayGridMonth/now" style="position:fixed; top:40px; left:0; bottom:10px; right:0; width:100%; height:95%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</div>

<div id="HomeAssistant2" class="tabcontent">
  <iframe src="https://172.16.0.42:8123/lovelace/index.htm" style="position:fixed; top:40px; left:0; bottom:10px; right:0; width:100%; height:95%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</div>


<div id="pCP-RB" class="tabcontent">
  <iframe scrolling="no" src="http://172.16.0.22/cgi-bin/main.cgi" style="position:fixed; border: 0px none; top:50px; margin-left: -36px; height: 602px; margin-top: -286px; width: 650px;">
  </iframe>
</div>

<script>
function openPage(evt, cityName) {
  var i, tabcontent, tablinks;
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
  }
  tablinks = document.getElementsByClassName("tablinks");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }
  document.getElementById(cityName).style.display = "block";
  evt.currentTarget.className += " active";
}
</script>

</body>
</html>

TBH: I don’t have a clue what the difference is in power usage but I suppose you can find that info on the web - or measure it.

Have you seen the topics here about e-ink paper displays?

That dedicated music player is at a spot where it can be seen from both living area & kitchen and that’s the only clock in that area so it’s the ideal solution for that purpose.
The software (piCorePlayer & Jivelite) works very well but the system can only do that of course.

Tip: I don’t use unclutter for the mouse pointer; I removed it from “/etc/lightdm/lightdm.conf” by changing the line “xserver-command=X” into “xserver-command=X -nocursor”
Unclutter uses quite a lot CPU cycles.

Just found out that you can use open.spotify.com to play music through my Sonos speakers but I does not seem to play nicely with iframes.

Anyone know how I can get it to work in an iframe.

This is more of a guide for me to save energy but hopefully someone will find it useful.

I have found a way of turning the power off to the screen and the raspberry pi and starting with a nice clean black screen until the website opens.

I have used the guide above but instead of turning just the screen off I have used a relay to control power to both the screen and the raspberry pi.

One of my main issues was the on screen messages that looked unprofessional.

I have removed the raspberry pi messages as follows:

Remove Rainbow Screen

sudo nano /boot/config.txt

and add:

disable_splash=1

Remove Raspberry Pi logo and blinking cursor

sudo nano /boot/cmdline.txt

and add

logo.nologo vt.global_cursor_default=0

Remove login message

touch ~/.hushlogin

Remove autologin message by modify autologin service

sudo nano /etc/systemd/system/[email protected]/autologin.conf

replace second line with

ExecStart=-/sbin/agetty --skip-login --noclear --noissue --login-options "-f pi" %I $TERM

Remove X Org X Server Output

sudo nano ~/.bash_profile

replace

startx

with

startx >/dev/null 2>&1

Finally I needed a display without OSD popups which I found here:

https://cpc.farnell.com/multicomp-pro/touchscreen-10-1/touch-screen-display-10-1-raspberry/dp/SC15602?ost=touchscreen+10.1+10.1"+hdmi+ips+lcd+display+kit+for+raspberry+pi&cfm=true&pm=true

I ideally did not want to spend and money but it could have been worse.

Guys, I have discovered another, quite simple way, to turn the display on & off:

'echo 1 | sudo tee /sys/class/backlight/rpi_backlight/bl_power'

&

'echo 0 | sudo tee /sys/class/backlight/rpi_backlight/bl_power'

Is this the official pi monitor or a generic one?
I assume this is using the pi display port or is this via HDMI and USB?

Official 7" touchscreen, connected to the DSI port from the RPi

Don’t think this would work with hdmi?

The one I quoted above was hdmi and usb.