6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 1
# Written by Sudharshan 'Sup3rkiddo' S
2
# released under WTFPL
3
4
# I know a simple mirror would suffice. But screw mirrors.
5
# The sound file taken from http://www.freesound.org/samplesViewSingle.php?id=72557
6
7
import datetime
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 8
import time
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 9
from datetime import datetime
10
from optparse import OptionParser
11
12
13
NOTIFICATIONS = True
14
try:
15
    import pynotify
16
    pynotify.init("Boss Alert")
17
except:
18
    print "Pynotify not installed. Notifications will be disabled"
19
    NOTIFICATIONS = False
20
    
21
22
import pygame
23
import pygame.camera
24
import pygame.font
25
import pygame.mixer
26
27
from pygame.locals import *
28
29
30
class BossAlert(object):
31
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 32
    def __init__(self, soundpath=None, waitfor=0):
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 33
        self.reference_color = None
34
        size = (640,480)        
35
        pygame.camera.init()
36
            
37
        self.display = pygame.display.set_mode(size, 0)
38
        cams = pygame.camera.list_cameras()
39
        assert cams, "No Cameras detected. Get a mirror instead"
40
41
        self.notify = None
42
        if NOTIFICATIONS:
43
            self.notify = pynotify.Notification("BossAlert", "Your Boss just walked down the hallway. Back to work")
44
            self.notify.set_urgency(pynotify.URGENCY_CRITICAL)
45
            self.notify.set_timeout(5)
46
        
47
        self.cam = pygame.camera.Camera(cams[0], size, "RGB24")
48
        self.cam.start()
49
        print "Initializing Camera"
50
        
51
        self.surface = pygame.surface.Surface(size, 0, self.display)
52
        self.font = pygame.font.SysFont("Courier New", 30, bold=True)
53
54
        self.sound = None
55
        if soundpath:
56
            print "Init pygame mixer"
57
            pygame.mixer.init()
58
            self.sound = pygame.mixer.Sound(soundpath)
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 59
        self.waitfor = waitfor
60
        self.starttime = time.time()
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 61
        self.refresh()
62
63
        
64
    def check_movement(self, rect, threshold=30):
65
        """Really dumb way to check for movement.
66
        Find out the average color within the rectangular area. If it differs from
67
        the reference colour by a certain threshold, we have movement.
68
        Simple but effective in cases where the background colour is uniform.
69
        """
70
        color = pygame.transform.average_color(self.surface, rect)
71
        diff = [a - b for a,b in zip(color, self.reference_color)]
72
        for d in diff:
73
            if abs(d) > threshold:
74
                return True
75
        return False
76
77
78
    def get_image(self, surface):
79
        success = self.cam.query_image()
80
        if success:
81
            return self.cam.get_image(surface)
82
        return None
83
84
    
85
    def refresh(self):
86
        snapshot = self.get_image(self.surface)
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 87
        if not snapshot:
88
            return
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 89
        # Change the position of the rectangular window here. Or position your webcam accordingly
90
        rect = pygame.draw.rect(snapshot, (255, 0, 0), (10, 40, 100, 100), 2)
91
        
92
        if not self.reference_color:
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 93
            if time.time() - self.starttime < self.waitfor:
94
                return
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 95
            self.reference_color = pygame.transform.average_color(snapshot, rect)
4948d14 by Sudharshan 'Sup3rkiddo' S at 2011-02-08 96
97
        # Play a sound and pop up a desktop notification if any motion is detected within the rect window
98
        if self.check_movement(rect):
99
            print "Movement detected at ", datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S")
100
            if not pygame.mixer.get_busy() and self.sound:
101
                self.sound.play(loops=-1)
102
            if NOTIFICATIONS:
103
                self.notify.show()
104
            snapshot.fill((255, 0, 0), rect)
105
            ren = self.font.render("Boss Alert", 30, (255, 0, 0))
106
            snapshot.blit(ren, (400, 200))
107
        else:
108
            if self.sound:
109
                self.sound.fadeout(500) 
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 110
        self.display.blit(snapshot, (0,0))
111
        self.surface = snapshot
112
        pygame.display.flip()
113
114
        
115
    def startloop(self):
116
        while True:
117
            events = pygame.event.get()
118
            for e in events:
119
                if e.type == QUIT:
120
                    self.cam.stop()
121
                    exit()
122
            self.refresh()
123
124
125
if __name__ == '__main__':
126
    pygame.init()
127
    oparser = OptionParser()
128
    oparser.add_option("-s", "--sound", dest="soundpath",
129
                       help="Path to the Sound file to be played when movement is detected", metavar="FILE")
130
    oparser.add_option("-x", "--disable-notifications", dest="no_notify", action="store_true", default=False,
131
                       help="Disable desktop notifications (Need pynotify to be installed)")
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 132
    oparser.add_option("-w", "--wait-for", dest="wait_for", default=0,
133
                       help="The time (milliseconds) after which the reference color should be captured")
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 134
    (options, args) = oparser.parse_args()
135
    NOTIFICATIONS = not options.no_notify
a46f0fa by Sudharshan 'Sup3rkiddo' S at 2011-02-08 136
    alert = BossAlert(soundpath=options.soundpath, waitfor=int(options.wait_for))
6d30d96 by Sudharshan 'Sup3rkiddo' S at 2011-02-06 137
    alert.startloop()