I am making a smart nightstand using a Raspberry Pi and Python. It should be able to:

  1. Have settings being controlled by a mobile app communicating through TCP requests to the Raspberry Pi
  2. Have an alarm go off at a given time. The alarm will play a sound/music and turn on an LED strip gradually increasing the brightness.
  3. Utilise buttons to either snooze/turn off the alarm or turn the LED strip on or off, depending on whether the alarm is currently on or not. The LEDs and buttons are controlled through the GPIO pins on the Raspberry Pi.

To keep the code clear I am trying to do some of the work in separate classes, so one class for managing the alarm, one for controlling the buttons, one for controlling the LED strip etc. The solution will be headless, all controlling done through TCP requests from the mobile app.

To control the buttons I do something like this:

import RPi.GPIO as GPIO # Import Raspberry Pi GPIO library
from enum import Enum

class TouchButton(Enum):
    DISABLE = 0
    ONOFF = 1
    MORE = 2
    LESS = 3
class ButtonHandler():
    disable_pin = #to be defined
    on_off_pin = #to be defined
    more_pin = #to be defined
    less_pin = #to be defined

        GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
        GPIO.add_event_detect(disable_pin,GPIO.RISING,callback=lambda x: button_callback(DISABLE), bouncetime=500)
        GPIO.add_event_detect(on_off_pin,GPIO.RISING,callback=lambda x: button_callback(ONOFF)), bouncetime=500)
        GPIO.add_event_detect(more_pin,GPIO.RISING,callback=lambda x: button_callback(MORE), bouncetime=20)
        GPIO.add_event_detect(less_pin,GPIO.RISING,callback=lambda x: button_callback(LESS), bouncetime=20)


    def button_callback(button):
        instance_of_another_class.some_function(button) #Somehow call back to an instance of another class

In the main class I would have something like this:

class MainApplicationClass():
    def config():

            pass #loop forever to keep script from stopping?
            #Or use sleep(forever?). The main point is: What do I do if the main code is just waiting for input from the alarm, button or LED control class instance and has no code to execute in the meantime?

    def some_function():
        pass #Expecting the call back when a button is pressed here

In addition to the above I would need to have code to handle the other tasks, such as monitoring for TCP requests, controlling the lights etc. These would all need to work in parallell, as a web request could be made while the alarm is going off, the buttons should be able to turn the alarm off while the sound is playing etc.

PS: The code above won't run, it is only meant to show the idea of what to do.

So, my issues/challenges are:

  1. The various features of the nightstand must be checked and controlled from a common object. For instance, when a button is pressed, I need to check if the alarm is active to determine if it should be turned off, or if the LEDs should be changed instead. How can for instance the button class "talk back" to the main class and tell it to take action based on a button press?
  2. Most/all of these tasks would probably need to be run in a separate thread (for instance, playing sound with pygame uses a while loop as long as the sound is playing, preventing any other code from being executed on the same thread like responding to TCP requests). If doing so, when the function is called in the main class instance again, is it thread safe or what would I have to do to make it so? I guess this one depends a bit on the solution for the first question.
  3. A lot of waiting for input in Python and when using Raspberry Pi seems to be based on infinite while loops. Is there really no better way of doing this? When running several background tasks this while loop waiting would probably require huge amount of unnecessary computing? For instance, if I offload the tasks (alarm, buttons etc) to other threads and the main thread is done executing the code in MainApplicationClass, will it terminate the application while waiting for callback from the other tasks since it has reached the end of the code?

I am completely new to Python and may approach this very badly. There might also be an obvious answer, but as I'm not very familiar with the syntax of Python I'm struggling to search for the right keywords. That being said, I've programmed in Objective-C and Swift for years, so the concept of programming is not unfamiliar, just the way of doing it in Python.

  • Could you use the gpiozero to do the monitoring and pop the actions required on a queue for a background task to action? Queues are thread safe and I limit the number of global variables and function calls used within the tasks. Most of my threads are sat in a sleep(0.5) command waiting for something to happen or an entry on a queue to arrive. This lets the cpu do something for some other program!
    – user115418
    Jun 17 '20 at 18:59
  • Thanks for all the input. I have now re-written the question to hopefully be more clear.
    – Jorn
    Jun 17 '20 at 20:06

This Question is too vague, asks multiple questions, is not Pi specific and is really asking how to program in Python, so will probably be closed.

However the use of Buttons and callbacks is relevant and Raspberry Pi OS includes an excellent Python library gpiozero which abstracts most of the nuts and bolts and allows effectively unlimited callbacks.

If you decide to use gpiozero read the FAQ first.

If you are new to Python avoid threads - they rarely offer much benefit, because Python is interpreted and the GIL stops things running in parallel.

Your point 3 is true, but only because new programmers are encouraged to use Python have never learned the art of programming.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.