I am fairly new to python and still learning so please be gentle on me. I have got my python script which subscribes to a MQTT topic and receives a code.
If the payload is equal to that code then it would set the timer which would turn on the display. After the time has elapsed the display would turn off until the payload is again equal to the same code as described previously.
Here is my code:
import paho.mqtt.client as mqttClient
import time
from subprocess import call
from time import sleep
def on_connect(client, userdata, flags, rc):
# print("Connected with result code "+str(rc))
client.subscribe("home/OpenMQTTGateway/433toMQTT")
#def on_message(client, userdata, msg):
# if msg.topic == 'home/OpenMQTTGateway/433toMQTT':
# print(str(msg.payload))
def on_message(client, userdata, msg):
global myGlobalMessagePayload
if msg.topic == 'home/OpenMQTTGateway/433toMQTT':
myGlobalMessagePayload = msg.payload
timeUntilDisplayOff = 240
timer = timeUntilDisplayOff
while True:
if msg.payload == '1381683':
timer = timeUntilDisplayOff
print ("Motion detected! Setting timer to " + str(timer) + " seconds.")
if timer > 0:
if timer % 10 == 0:
print ("Timer: " + str(timer) + " seconds")
timer -= 1
elif timer == 0:
call(['vcgencmd', 'display_power', '0'])
print ("Timer is 0. Display turned off. Waiting for motion...")
# display is now off. we wait for motion and turn it on
myGlobalMessagePayload == '1381683'
call(['vcgencmd', 'display_power', '1'])
timer = timeUntilDisplayOff
sleep(1)
Problem:
When I run my code, I get an error saying:
Traceback (most recent call last):
File "test2.py", line 26, in <module>
if msg.payload == '1381683':
NameError: name 'msg' is not defined
Also I have used the generic code to repeat the process i.e. near the end after # display is now off. we wait for motion and turn it on, does it look correct? or can it be made any better?
Thanks
UPDATE:
So I sorted my code thanks to #blt. However my code keeps on looping when the msg.payload matches '1381683'. I get the following:
Motion detected! Setting timer to 240 seconds.
Timer: 240 seconds
Motion detected! Setting timer to 240 seconds.
Timer: 240 seconds
Motion detected! Setting timer to 240 seconds.
Timer: 240 seconds
the above keeps on looping...
Indentation is important in python.
You need to indent the following block by one level:
timeUntilDisplayOff = 1800
timer = timeUntilDisplayOff
while True:
Otherwise the code that follows this section will not have the msg variable in scope (hence the error).
So the first half of the on_message function is:
def on_message(client, userdata, msg):
global myGlobalMessagePayload
if msg.topic == 'home/OpenMQTTGateway/433toMQTT':
myGlobalMessagePayload = msg.payload
timeUntilDisplayOff = 1800
timer = timeUntilDisplayOff
while True:
if msg.payload == '1381683':
timer = timeUntilDisplayOff
print ("Motion detected! Setting timer to " + str(timer) + " seconds.")
Related
As a new programmer, I'm trying to create a Python3 script that creates a Countdown timer based on the KeyCombination used which is then written to a text file used by StreamlabsOBS. The idea is that when a KeyCombo is pressed (e.g ctrl+alt+z) a function starts a timer, and at the same time, another function writes the current time in the countdown to a txt file. This script would ideally be running during the entire stream.
So far, I can get the countdown working and writing to the file exactly how I want it to. But it seems the Thread is still alive after finishing the countdown. I'm not sure if this is a problem or not. Another thing is that I want to implement some kind of pause feature, that would pause the timer function. I'm not sure how to even start on that part.
The print statements are for me to know what part of the function I am at.
from pynput import keyboard
from pathlib import Path
from threading import Thread
import queue
from time import sleep
script_location = Path(__file__).absolute().parent
timer_queue = queue.Queue()
def storeInQueue(function):
print("Sent Start Thread msg")
def storer(*args):
for time in function(*args):
timer_queue.put(time)
print(f"stored {time}")
return storer
#storeInQueue
def timer(t):
print("Iterating Timer Loop")
while t > -1:
yield t
sleep(1)
t -= 1
def speakKorean():
print("starting Thread")
timer_thread = Thread(target=timer, args=(5,))
timer_thread.start()
ctime = timer_queue.get()
while ctime >-1:
with open(script_location / 'ChronoDown.txt', "w") as timer_file:
timer_file.write(f"Speak Korean for {ctime}s")
timer_file.flush()
sleep(1)
ctime = timer_queue.get()
if ctime == 0: break
print('Speak Korean done!')
with open(script_location / 'ChronoDown.txt', "w") as timer_file:
timer_file.write(f"Done!")
timer_file.flush()
while timer_thread.is_alive:
print("timer thread still running?")
timer_thread.join()
break
if timer_thread.is_alive:
print("didn't work")
def on_activate_z():
timer_file = open(script_location / 'ChronoDown.txt', "w")
timer_file.write("other keywords")
timer_file.close()
def on_activate_c():
korean_thread = Thread(target=speakKorean,)
korean_thread.start()
print("Working")
def on_activate_x():
timer_file = open(script_location / 'ChronoDown.txt', "w")
timer_file.write("No cursing for time")
timer_file.close()
with keyboard.GlobalHotKeys({
'<ctrl>+<alt>+z': on_activate_z,'<ctrl>+<alt>+c': on_activate_c,
'<ctrl>+<alt>+x': on_activate_x}) as h:
h.join()
My console output looks like this after I run it. I'm not sure why "Sent Start Thread msg" sends before I start thread too.
Sent Start Thread msg
starting Thread
Working
Iterating Timer Loop
stored 5
stored 4
stored 3
stored 2
stored 1
stored 0
Speak Korean done!
timer thread still running?
didn't work
Also if you have any optimization tips that would be appreciated. Thank you in advance for any help.
Thanks to #furas , I've now implemented a pause function that properly resumes as well. This is my updated code :
from pynput import keyboard
from pathlib import Path
from threading import Thread
import queue
from time import sleep
script_location = Path(__file__).absolute().parent
timer_queue = queue.Queue()
paused = False
while paused == False:
def storeInQueue(function):
print("Sent Start Thread msg")
def storer(*args):
for time in function(*args):
timer_queue.put(time)
print(f"stored {time}")
return storer
#storeInQueue
def timer(t):
print("Iterating Timer Loop")
while t > -1:
if paused == False:
yield t
sleep(1)
t -= 1
else: continue
def speakKorean():
print("starting Thread")
timer_thread = Thread(target=timer, args=(5,))
timer_thread.start()
ctime = timer_queue.get()
while ctime >-1:
with open(script_location / 'ChronoDown.txt', "w") as timer_file:
timer_file.write(f"Speak Korean for {ctime}s")
timer_file.flush()
sleep(1)
ctime = timer_queue.get()
if ctime == 0: break
print('Speak Korean done!')
with open(script_location / 'ChronoDown.txt', "w") as timer_file:
timer_file.write(f"Done!")
timer_file.flush()
timer_thread.join()
if timer_thread.is_alive():
print("didn't work")
else: print("its dead")
def on_activate_z():
global paused
timer_file = open(script_location / 'ChronoDown.txt', "w")
timer_file.write("Paused")
timer_file.close()
if paused == True:
paused = False
print(f'Paused = {paused}')
else:
paused =True
print(f'Paused = {paused}')
def on_activate_c():
korean_thread = Thread(target=speakKorean,)
korean_thread.start()
print("Working")
def on_activate_x():
timer_file = open(script_location / 'ChronoDown.txt', "w")
timer_file.write("No cursing for time")
timer_file.close()
with keyboard.GlobalHotKeys({
'<ctrl>+<alt>+z': on_activate_z,'<ctrl>+<alt>+c': on_activate_c,
'<ctrl>+<alt>+x': on_activate_x}) as h:
h.join()
The main differences:
My entire code is now encapsulated in a While paused == False loop, which allows me to pause my timer function based on the state of paused using an if statement
I've added the missing ( ) to timer_thread.is_alive() which allowed me to properly end the timer Thread
So I'm doing some testing with threads and I realised I could not stop and then start a thread. I could stop it, but starting it again was the issue.I want a script that adds 1 to a var when it is on then its stops when off by pressing shift to turn on and off.I have the detecting shift working (it is on another part of my code), but I just need to find out how to stop and start threadsHere is my test code:
from threading import Thread as th
import time as t
var = 0
def testDef():
global var
var += 1:
t.sleep(1)
test = th(target = testDef)
test.start()
while True:
menu = input("On, Off, Show Var")
if menu == "On":
test.start()
elif menu == "Off":
test._stop():
elif menu == "S":
print(var)
I know there are a few errors, but I mainly need the on and off threading to work.
Thanks, Jeff.
As far as I know, you can't actually stop and restart a thread as you can't use test.start() when the method has been terminated. However, you may be wondering to something similar by using threading.Condition to pause and later resume the execution.
You can read more about it in the documentation.
There is also an error in var += 1:, change it to var += 1
Here's a simple example on how to use threading.Event to enable two threads to communicate. This works by setting the internal flag of the Event to either True or False. While this internal flag is False you can ask thread a to wait (effectively block, which is not very efficient by the way). Then we use the two timers (b, c) to simulate a shift press every 5 seconds. In order to release a we set the event (internal flag = True). 5 seconds later, we clear the value of the internal flag and this will make thread a to block again.
import threading
def do(event):
flag = True
while flag:
if not event.isSet():
print "blocking"
event.wait()
else:
print "resuming"
def pressShift(event, enable):
print "Shift pressed"
if enable:
event.set()
else:
event.clear()
def main():
event = threading.Event()
a = threading.Thread(target=do, args=(event,))
b = threading.Timer(5, pressShift, args=(event, True)).start()
c = threading.Timer(10, pressShift, args=(event, False)).start()
a.start()
a.join()
if __name__ == "__main__":
main()
You cannot restart a thread that has already been started. What you can do, however, is to create another thread.
from threading import Thread as th
import time as t
var = 0
def testDef():
global var
var += 1
t.sleep(1)
test = th(target = testDef)
test.start()
while True:
menu = input("On, Off, Show Var")
if menu == "On":
test = th(target = testDef)
test.start()
elif menu == "Off":
test._stop()
elif menu == "S":
print(var)
Use an event object like this post, and check that event in your target functoin. Also, you need a new thread each time you re-start. The code shown below adds some debugging that should be useful. (Another approach is to build a custom stop function.)
import logging
import threading
import time as t
var = 0
logging.basicConfig(level=logging.DEBUG,
format='[%(levelname)s] (%(threadName)-10s) %(message)s',
)
def testDef(stop_event):
global var
print 'Thread Running', var
# inThread.stop()
while not stop_event.isSet():
var += 1
logging.debug('Var is %i' % var)
t.sleep(1)
# Use an event to track user input
testStopEvent = threading.Event()
testStopEvent.clear()
test = threading.Thread(name = 'test', target=testDef, args=((testStopEvent,)))
test.setDaemon(True)
while True:
menu = input("On = 1, Off = 2, Show Var = 3")
if menu == 1:
test.start()
elif menu == 2:
testStopEvent.set()
test.join() # Wait for the thread to finish
test = threading.Thread(target=testDef, args=((testStopEvent,))) # "re-start" thread
testStopEvent.clear() # Reset the stop event
elif menu == 3:
print(var)
I'm trying to create a timer that starts when a condition for an if statement is met and then stops and returns the duration when elif condition is met, is this possible?
The purpose of the application is to send a true or false value to AWS IoT from an Android application. The Python script is subscribed to AWS and receives the value and uses it to determine whether the led should be on or off.
The code I have:
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import RPi.GPIO as GPIO
import sys
import logging
import time
from time import sleep
from timeit import default_timer as timer
import getopt
import grovepi
msgpay = None
# Custom MQTT message callback
def customCallback(client, userdata, message):
print("Received a new message: ")
print(message.payload)
print("from topic: ")
print(message.topic)
print("--------------\n\n")
global msgpay
msgpay = message.payload
led = 5
grovepi.pinMode(led, "OUTPUT")
start_time = timer()
if msgpay == "true":
print("turning on")
grovepi.digitalWrite(led, 1)
#time.sleep(3)
elif msgpay == "false":
print("turning off")
grovepi.digitalWrite(led, 0)
duration = timer() - start_time
print duration
Any help how to go about this would be appreciated.
Thanks
I'm just start learning about python and I have problem with my project to blink LED. when I get new message and start new thread. The old thread is still running.
I want to kill old thread and start new thread. How to solve my problem?
(Sorry if I'm not good in english but I'm trying)
def led_action(topic,message):
print topic+" "+message
if message == 'OFF':
#state = False
print ("Stoping...")
while message == 'OFF':
GPIO.output(8,GPIO.LOW)
elif message == 'ON':
#state = True
print ("Opening...")
while message == 'ON':
GPIO.output(8,GPIO.HIGH) #Set LED pin 8 to HIGH
time.sleep(1) #Delay 1 second
GPIO.output(8,GPIO.LOW) #Set LED pin 8 to LOW
time.sleep(1)
# Get message form NETPIE and Do something
def subscription(topic,message):
set = thread.start_new_thread(led_action, (topic,message))
def connection():
print "Now I am connected with netpie"
def disconnect():
print "disconnect is work"
microgear.setalias("test")
microgear.on_connect = connection
microgear.on_message = subscription
microgear.on_disconnect = disconnect
microgear.subscribe("/mails")
microgear.connect(True)
To terminate a python thread you need to exit your function. You can do this by removing your while message == 'ON'/'OFF' checks. As message doesn't change anyways (it is passed to the function led_action) those checks are unnecessary.
Could anybody advise me on converting the Java Timer class to Python? Currently I am converting a Java program to a Python script. However, Python does not have the Timer/TimerTask library (if it does have this, please enlighten me. Thanks!). I need to be able to reset the Timer. Java has Timer.cancel, but Python doesn't have this. Is there any replacement for it?
timer.cancel();
timer = new Timer("Printer");
MyTask t = new MyTask();
timer.schedule(t, 0, 1000);
Java script timer
class Timerclass extends TimerTask {
//times member represent calling times.
private int times = 0;
public void run() {
times++;
if (times <= 5) {
System.out.println(""+times);
} else {
this.cancel();
//Stop Timer.
System.out.println("Timer Finish");
}
}
}
Currently my code
import time
import threading
class Variable:
count = 0
people = 0
times = 0
def enter():
if int(Variable.count == 1):
print("Entered")
t = threading.Timer(5.0, countdown)
t.start()
else:
print("Entered +1")
t.clear() // Stuck Help
t = threading.Timer(5.0, countdown)
t.start()
def out():
if int(Variable.count > 0):
print("Exited")
elif int(Variable.count < 0):
print("Error")
def countdown():
print("TIMEUP")
while True:
sensor1 = input("Sensor 1: ")
sensor2 = input("Sensor 2: ")
Variable.count+=1
if int(sensor1) == int(sensor2):
Variable.count -= 1
print(Variable.count)
print("error")
elif int(sensor1) == 1:
Variable.people += 1
print(Variable.people)
enter()
elif int(sensor2) == 1:
Variable.people -= 1
print(Variable.people)
out()
else:
print("Error")
i have one problems that i'm stuck in i need to stop the current counting and start a new one whenever the method call
Basically what i want or im looking out for is when i recall this method it will reset or cancel any existing and recount again
Update latest
import time
import threading
class Variable:
count = 0
people = 0
times = 0
def countdown():
print("TIMEUP")
t = threading.Timer(5.0, countdown)
def enter():
if int(Variable.count == 1):
print("Entered")
t.start()
else:
print("Entered +1")
t.cancel()
t.join() # here you block the main thread until the timer is completely stopped
t.start()
def out():
if int(Variable.count > 0):
print("Exited")
elif int(Variable.count < 0):
print("Error")
while True:
sensor1 = input("Sensor 1: ")
sensor2 = input("Sensor 2: ")
Variable.count+=1
if int(sensor1) == int(sensor2):
Variable.count -= 1
print(Variable.count)
print("error")
elif int(sensor1) == 1:
Variable.people += 1
print(Variable.people)
enter()
elif int(sensor2) == 1:
Variable.people -= 1
print(Variable.people)
out()
else:
print("Error")
Anybody can spot my ,istake im getting this error but i t.clear() the process
in start raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once
I would suggest using the time module for something like this:
from time import time, sleep
from datetime import datetime, timedelta
nowtime = time()
#Put your script here
x = 1
for k in range(1000):
x+=1
sleep(0.01)
sec = timedelta(seconds=int(time()-nowtime))
d = datetime(1,1,1)+sec
print("DAYS:HOURS:MIN:SEC")
print("%d:%d:%d:%d" % (d.day-1, d.hour, d.minute, d.second))
This assigns the time in seconds at the beginning to a variable, and after the main script has finished, it subtracts the previous time from the current time and formats it in days, hours, minutes, and seconds.
Here is it running:
bash-3.2$ python timing.py
DAYS:HOURS:MIN:SEC
0:0:0:10
bash-3.2$
You could also use the Threading module, which has a built-in cancel method:
>>> import threading
>>> def hello():
... print "This will print after a desired period of time"
...
>>> timer = threading.Timer(3.0, hello)
>>> timer.start() #After 3.0 seconds, "This will print after a desired period of time" will be printed
>>> This will print after a desired period of time
>>> timer.start()
>>> timer = threading.Timer(3.0, hello)
>>> timer.start()
>>> timer.cancel()
>>>
Python actually has a class for this, which includes a cancel method: threading.Timer. It seems to be close enough to the Java Timer class for your needs (The Java Timer also runs in background thread). Here's the example usage from the docs:
def hello():
print "hello, world"
t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
Edit:
The problem with your updated code is that you're trying to use the same Timer object more than once. That may be possible in the Java implementation, but in Python you can't reuse a Thread object (Timer is a Thread subclass). You'll need to create a new Timer object after you join() it. Like this:
t = threading.Timer(5.0, countdown)
def enter():
global t # You need this to tell Python that you're going to change the global t variable. If you don't do this, using 't = ..' will just create a local t variable.
if int(Variable.count == 1):
print("Entered")
t.start()
else:
print("Entered +1")
t.cancel()
t.join() # here you block the main thread until the timer is completely stopped
t = threading.Timer(5.0, countdown)
t.start()