I'm trying to start a dbus timer from python.
At the moment I was able to launch it through this script:
import dbus
from subprocess import call
def scheduleWall( time, message ):
call(['systemd-run --on-active='+str(time) +' --unit=scheduled-message --description="'+ message +'" wall "'+ message +'"'], shell=True)
I'd like to not use "call", but try to use "StartTransientUnit", but I wasn't able to understand the format of the call at all! I'm rather new to dbus and python.
def scheduleWall( time, message ):
try:
bus = dbus.SystemBus()
systemd1 = bus.get_object("org.freedesktop.systemd1"," /org/freedesktop/systemd1")
manager = dbus.Interface(systemd1, 'org.freedesktop.systemd1.Manager')
obj = manager.StartTransientUnit('scheduled-message.timer','fail',[????],[????])
except:
pass
Is startTransientUnit the right method to call? how should I call it?
TL;DR: stick to systemd-run :)
I don’t think StartTransientUnit is quite the right method – you need to create two transient units, after all: the timer unit, and the service unit that it will start (which will run wall later). Perhaps you can use StartTransientUnit for the timer, but at least not for the service. You also need to set all the properties that the two units need (OnActiveSec= for the timer, ExecStart= for the service, probably some more…) – you can see how systemd-run does it by running busctl monitor org.freedesktop.systemd1 and then doing systemctl run --on-active 1s /bin/true in another terminal. (The main calls seem to be UnitNew and JobNew.)
I’ll admit, to me this seems rather complicated, and if systemd-run already exists to do the job for you, why not use it? The only change I would make is to eliminate the shell part and pass an array of arguments instead of a single space-separated string, with something like this (untested):
subprocess.run(['systemd-run', '--on-active', str(time), ' --unit', 'scheduled-message', '--description', message, 'wall', message)
Related
I am trying to display RSS data on an LED sign using a Raspberry PI. I've based my code on a script that I found for the sign when I first bought it. It's a simple script that allows you to send a message and a colour to the sign and it will scroll across until a keyboard interrupt.
sudo python scroll "Hello World" 1 #red
sudo python scroll "Hello World" 2 #green
sudo python scroll "Hello World" 3 #red and green (orange)
The difference between this script and the one that I am working on is that all the all the data is processed before the loop and then the showmatrix() function is used to show the string on the screen and the shiftmatrix() function is used to scroll the image across.
In order to constantly download the RSS data I have put the following code inside the loop:
#grab emails
newmails = int(feedparser.parse("https://" + USERNAME + ":" + PASSWORD +"#mail.google.com/gmail/feed/atom")["feed"]["fullcount"])
textinput = "You have " + str(newmails) + " new emails"
# append extra characters to text input to allow for wrap-around
textinput+=" :: "
I then use the same functions as before to display this data on the sign:
# Continually output to the display until Ctrl-C
#
# loop around each column in the dotarray
for col in range(len(dotarray[0])):
for row in range(8):
# copy the current dotarray column values to the first column in the matrix
matrix[row][0]=(dotarray[row][col])
# now that we have updated the matrix lets show it
showmatrix()
# shift the matrix left ready for the next column
shiftmatrix()
As the RSS data download takes so long (at last a second), the output loop doesn't run for that time and the sign goes blank. Is there a way of running the feedparser function at the same time so there is no delay?
Am I correct in thinking that multithreading is the way forward? I had a look into couroutines but that got me nowhere.
Yes, os.fork(), youcan make the function run in a different process or the threading module to make it run in another thread.
If the function uses global variables you need to use the threading module and make it run in another thread and if not i'd suggest to do it anyway, less resource wasteful (assuming the function doesnt allocate alot of memory or otherwise uses alot of resources), you code should look something like this:
class displayThread(threading.Thread)
*init function if you need to pass info to the tread, otherwise dont write one but if you do
make sure to call Thread.__init__() first in your function*
def run(): //Overrides the run function
*display what you want on the display*
class downloadThread(threading.Thread)
*init function if you need to pass info to the tread, otherwise dont write one but if you do
make sure to call Thread.__init__() first in your function*
def run(): //Overrides the run function
*download what you want*
and your main script should look like:
thread1 = displayThread
thread2 = downloadThread
thread1.start()
thread2.start()
thread2.join() //waits for download to finish while the display in being updated by the other thread
and if you want to stop the display thread (assuming it goes on forever) you will have to add something like:
os.kill(thread1.getpid(), signal.SIGKILL)
after the .join() and do what you want with the downloaded info.
The multi process version is very similar and you should be able to understand how to make it from my example and the os.fork() docs, if you are having trouble with it - let me know and i'll edit this.
Hi I am experimenting with Speech Synthesis on mac, and I always put while loops in my programs so that I can use them until I decide to stop, and with this code, it repeats "What would you like me to say?" At the same time it says whatever I tell it to say.
from Cocoa import NSSpeechSynthesizer
while 1==1
sp = NSSpeechSynthesizer.alloc().initWithVoice_(None)
sp.startSpeakingString_("What would you like me to say?")
say_1 = raw_input("What would you like me to say?")
sp.startSpeakingString_(say_1)
Can someone tell me how to tell python to wait until it is done saying what I tell it to?
It seems you are looking for NSSpeechSynthesizer instance method: isSpeaking. You can write a polling loop to test if it is speaking and continue to work once it is not anymore. Something like this:
import time
from Cocoa import NSSpeechSynthesizer
while 1:
sp = NSSpeechSynthesizer.alloc().initWithVoice_(None)
sp.startSpeakingString_("What would you like me to say?")
say_1 = raw_input("What would you like me to say?")
sp.startSpeakingString_(say_1)
while sp.isSpeaking(): # loop until it finish to speak
time.sleep(0.9) # be nice with the CPU
print 'done speaking'
UPDATE: Is better time.sleep than continue inside the loop. The latter will waste a lot of CPU and battery (as pointed out by #kindall).
Hope this helps!
The problem is that the speech API does the speaking asynchronously. I don't know anything about this particular API, but to get this code working you'd have to poll in a loop or find an argument that specifies that your call should block. This issue is specifically connected to way this API works.
For this task, assuming you're using a Mac, you could use the command line instead. This will wait for the speech to finish before continuing.
import subprocess
def say(text):
subprocess.call(["say", text])
print("Before")
say("Wait for me!")
print("After")
I'm currently using Popen to send instructions to a utility (canutils... the cansend function in particular) via the command line.
The entire function looks like this.
def _CANSend(self, register, value, readWrite = 'write'):
"""send a CAN frame"""
queue=self.CANbus.queue
cobID = hex(0x600 + self.nodeID) #assign nodeID
indexByteLow,indexByteHigh,indexByteHigher,indexByteHighest = _bytes(register['index'], register['objectDataType'])
subIndex = hex(register['subindex'])
valueByteLow,valueByteHigh,valueByteHigher,valueByteHighest = _bytes(value, register['objectDataType'])
io = hex(COMMAND_SPECIFIER[readWrite])
frame = ["cansend", self.formattedCANBus, "-i", cobID, io, indexByteLow, indexByteHigh, subIndex, valueByteLow, valueByteHigh, valueByteHigher, valueByteHighest, "0x00"]
Popen(frame,stdout=PIPE)
a=queue.get()
queue.task_done()
return a
I was running into some issues as I was trying to send frames (the Popen frame actually executes the command that sends the frame) in rapid succession, but found that the Popen line was taking somewhere on the order of 35 ms to execute... every other line was less than 2 us.
So... what might be a better way to invoke the cansend function (which, again, is part of the canutils utility..._CANSend is the python function above that calls ) more rapidly?
I suspect that most of that time is due to the overhead of forking every time you run cansend. To get rid of it, you'll want an approach that doesn't have to create a new process for each send.
According to this blog post, SocketCAN is supported by python 3.3. It should let your program create and use CAN sockets directly. That's probably the direction you'll want to go.
I've been playing around with the pybluez module recently to scan for nearby Bluetooth devices. What I want to do now is extend the program to also find nearby WiFi client devices.
The WiFi client scanner will have need to have a While True loop to continually monitor the airwaves. If I were to write this as a straight up, one file program, it would be easy.
import ...
while True:
client = scan()
print client['mac']
What I want, however, is to make this a module. I want to be able to reuse it later and, possible, have others use it too. What I can't figure out is how to handle the loop.
import mymodule
scan()
Assuming the first example code was 'mymodule', this program would simply print out the data to stdout. I would want to be able to use this data in my program instead of having the module print it out...
How should I code the module?
I think the best approach is going to be to have the scanner run on a separate thread from the main program. The module should have methods that start and stop the scanner, and another that returns the current access point list (using a lock to synchronize). See the threading module.
How about something pretty straightforward like:
mymodule.py
import ...
def scanner():
while True:
client = scan()
yield client['mac']
othermodule.py
import mymodule
for mac in mymodule.scanner():
print mac
If you want something more useful than that, I'd also suggest a background thread as #kindall did.
Two interfaces would be useful.
scan() itself, which returned a list of found devices, such that I could call it to get an instantaneous snapshot of available bluetooth. It might take a max_seconds_to_search or a max_num_to_return parameter.
A "notify on found" function that accepted a callback. For instance (maybe typos, i just wrote this off the cuff).
def find_bluetooth(callback_func, time_to_search = 5.0):
already_found = []
start_time = time.clock()
while 1:
if time.clock()-start_time > 5.0: break
found = scan()
for entry in found:
if entry not in already_found:
callback_func(entry)
already_found.append(entry)
which would be used by doing this:
def my_callback(new_entry):
print new_entry # or something more interesting...
find_bluetooth(my_callback)
If I get your question, you want scan() in a separate file, so that it can be reused later.
Create utils.py
def scan():
# write code for scan here.
Create WiFi.py
import utils
def scan_wifi():
while True:
cli = utils.scan()
...
return
I have written this short script (which I've stripped away some minor detail for size) and I'm getting a very simple error, yet, I don't understand why! I'm very new to Python, so maybe someone can explain the issue and why it's not working?
The error seems to fall when I wish to print the full custom serial write string back to the console, it doesn't seem to recognise the Args I sent to the function.
Perhaps I have misunderstood something very simple. Should be simple for anyone even with the tiniest of Python understanding
Cheers
The Code:
#! /usr/bin/env python
# IMPORTS APPEAR HERE ***
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=115200,
parity='N',
stopbits=1,
bytesize=8
)
# Sets motor number
motor_no = "2"
# Lets create our main GUI class
class ArialApp(object):
# Default init stuff
def __init__(self):
# Create a builder object and create the objects from the .glade file
self.builder = gtk.Builder()
self.builder.add_from_file("../res/main.glade")
self.builder.connect_signals(self)
# Open the serial connection to the encoder and keep it open
ser.open()
# Custom function for sending commands down the serial. Needed to wrap defaults
# arround the custom 'serial.write' command.
self.send_command('A')
# Code removed for space.....
# Custom method for sending commands down serial with default ammendments
def send_command(self, nanotech):
# Send the command with the #, then motor number which should be global, then the command
# sent the the method followed by a return
ser.write("#" + motor_no + nanotech + '\r\n')
# Print to the console the full command sent down the pipe
# [[[ ERROR GOES HERE ]]]
print "#" + motor_no + nanotech + '\r\n'
# Just to show its in here...
if __name__ == "__main__":
app = ArialApp()
gtk.main()
The error:
File "main.py", line 62, in ArialApp
print "#" + motor_no + commands + '\r\n'
NameError: name 'commands' is not defined
Finally, just to shed some context on the situation:
I am writing a small GUI app in Glade and Python / PyGTK to control a stepper motor over serial using the PySerial module. However, I would like to package up my own "write" function so I can append default values to the 'send' down the cable. For example, the motor number and always appending returns on the end of the instructions. Other things like reading back the response straight away in the same function would be useful to gauge responses too, so, wrapping it up into a custom function seemed like the sensible thing to do.
Any advice or help on the above would be appreciated.
Thank-you kindly.
Andy
UPDATE: I have addresses the original issue of not including "self" and I've managed to get Stack to accept the tabs I normally use so its cleaner to look at. Also wanted to note the only code I removed was simple variable setting. However, the issue persists!
It could be because you're missing the self argument:
def send_command(self, commands):
you've got an indentation error in def send_command(commands):
and your first parameter should be "self" :
class ArialApp(object):
<snap>
def send_command(self, commands):
ser.write("#" + motor_no + commands + '\r\n')
Firstly, you should use more than a single space for indentation. White space is significant in Python, and it's very hard to see that you've got it right if you're only using one space. Four is the usually accepted amount.
The main issue with your send_command method is that you've forgotten that the first argument to any method in Python is (by convention) self. So the signature should be:
def send_command(self, commands):
However, the code you have shown would not give the error you state: it would instead give this:
TypeError: send_command() takes exactly 1 argument (2 given)
In addition, in your method it's not commands which is not defined, but motor_no. This is why it's always important to show the actual code you're running, cut down enough to actually reproduce the error.