The Dog Project

The arrival of a dog in the apartment has brought about a small issue. The dog is a little sensitive and doesn’t like to be left alone. Once alone, he’ll start whining, then barking incessantly.

He’s a cute dog, but a bit needy, and we can’t have him doing this while we’re at work and he’s home alone bothering the neighbors.

To combat the incessant barking, I’m trying out a technological solution. I recorded the girlfriend saying a variety of commands to tell him to stop barking, and I wrote a program that would listen to the microphone and play one of those sounds if the volume got too high. If he does it too many times, a text message is sent so that we know he’s being too loud for too long and that the neighbors might start to get annoyed.

The microphone is a pretty standard microphone. Any will do. Because my computer is upstairs and the dog downstairs, the microphone hangs over the side of the loft and down to the ceiling, pointing slightly out.

The software itself is simple as well. I wrote it in Python (my first Python application in fact!), and it’s running on an Ubuntu desktop. There’s a small interface that shows the sound level, and a slider for setting the threshold. If the sound level gets above the threshold, it’ll pick one of the sound files and play it. There’s a setting so it’ll only play a sound once every ten seconds, so it won’t go every time he makes a noise. If it goes too many times, it’ll send the text message.

So far, we’re still in the testing phase. It appears to do exactly what I programmed it to do, but we don’t really want to make the dog bark unnecessarily, so we’ll wait to see if it works as well as I think it will.

Here’s the code. It only took a couple hours, which isn’t bad considering this is my first Python app, my first Linux app, and my first time working with the microphone and speakers:

import alsaaudio, time, audioop, datetime, pygame, random, smtplib, email
from Tkinter import *
from email.mime.text import MIMEText

#email setup
emailfrom = "myemailaddress@bobbaddeley.com"
emailto = "myphonenumber@txt.att.net"

#set up the microphone
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK)
inp.setchannels(1)
inp.setrate(8000)
inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
inp.setperiodsize(160)

#set up the audio player
pygame.init()

#how many seconds between commands
#we don't want to say something every time there's a bark, so we only
#allow it to happen every n seconds at most
timeoutperiod = 10

#set up the last time we played the sound (-timeoutperiod so we can play it
#immediately if necessary)
lastplayed = time.time()-timeoutperiod

#start to set up the window
root = Tk()

#a label so we can track when the timeout period has expired
timer = Label(root, text="Time before reset:")
timer.pack()

#a label so we can look at the current audio level (useful for deciding what
#to use for the trigger level
level = Label(root, text="Audio level:")
level.pack()

#a slider for setting the trigger level
slider = Scale(root,from_=0, to=200, orient=HORIZONTAL, length=200)
#in my apartment the ambient noise is at 12, so setting it to 14 will work)
slider.set(14)
slider.pack()

#number of times the trigger has been set off
badcount = 0

#the number of times the trigger gets set off before we send an email
badcountlimit = 10

#label for the number of times the trigger has been set off
badcountlabel = Label()
badcountlabel.pack()

#called every few milliseconds
def tick():
	global lastplayed, inp, badcount,badcountlimit,timeoutperiod
	global emailfrom, emailto
	now = time.time()
	# Read data from device
	l,data = inp.read()
	if l:
		# Return the maximum value of all samples in a fragment.
		# Divide by 100 and compare to the slider value. If it's below
		# the trigger then we know we have to do something
		if (audioop.max(data,2)/100>(slider.get())):
			#if we haven't played a sound in a while
			if (now-lastplayed>timeoutperiod):
				#update some variables
				badcount = badcount+1
				lastplayed = now
				#pick which file to say
				numtosay = random.randrange(1,3,1)
				pygame.mixer.music.load("baddog_"+str(numtosay)+".ogg")
				#and play it				
				pygame.mixer.music.play()
				badcountlabel.config(text="Trigger Count:"+str(badcount))
				#if we've hit our limit, send an email				
				if (badcount==badcountlimit):
					server = smtplib.SMTP("mymailserver")
					msg = MIMEText("The dog has triggered the barking limit.")
					msg['Subject'] = 'Bad Dog'
					msg['From'] = emailfrom
					msg['To'] = emailto
					server.sendmail(emailfrom, emailto, msg.as_string())
					server.quit()
		else:
			#show the current audio level
			level.config(text="Audio level:"+str(audioop.max(data,2)/100))
	#show how long until the timeoutperiod is over
	if ((now-lastplayed)<timeoutperiod):	
		timer.config(text="Time before reset:"+str(round(timeoutperiod-(now-lastplayed))))
	root.after(10, tick)
tick()
root.title("Dog Sitter")
root.mainloop()
 
Copyright 2007, Bob Baddeley