#!/usr/bin/python
# -*- coding: utf-8 -*-

#
# January 2012 - v1.0
# wistof@gmail.com
#

from datetime import *
import os
import subprocess
import struct
import sys
import time
import locale
import ctypes

import smtplib




''' Define the X11 mouse ID and mouse name here

use command :  
$ xinput list | grep "slave  pointer" 
⎜   ↳ Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad              	id=10	[slave  pointer  (2)]
⎜   ↳ PS/2 Generic Mouse                      	id=11	[slave  pointer  (2)]
⎜   ↳ Kensington Kensington PocketMouse Pro   	id=13	[slave  pointer  (2)]

 '''
device_name = 'Kensington Kensington PocketMouse Pro'
xinput_id = "17"



''' Map a mouse boutton to a external sensor (label) '''
mouse_key_dic = {
	272: "Stock",   # Left button
	273: "Stock 2" ,  # Right button
	274: "Stock 3"  , # middle button
} 

''' Map mouse buttons events '''
mouse_event_dic = {
	0: "open", # button release => Door Opened
	1: "close", # button press => Door closed
} 

''' Define here the opening hours '''
days_off_dic =  {
	272 :
		{
			0: [("9:00","13:30") , ("14:00","20:00")  ] , # Monday
			1: [("9:00","13:30") , ("14:00","20:00")  ] , # Tuesday
			2: [("9:00","13:30") , ("14:00","20:00")  ]  , #Wednesday
			3: [("9:00","13:30") , ("14:00","20:00")  ] , # Thursday
			4: [("9:00","13:30") , ("14:00","20:00")  ] , # Friday
			5: [("9:00","13:30") , ("14:00","20:00")  ] , # Saturday
			0: [  ], # Sunday
		},
	273 :
		{
			0: [("9:00","13:30") , ("14:00","20:00")  ] , # Monday
			1: [("9:00","13:30") , ("14:00","20:00")  ] , # Tuesday
			2: [("9:00","13:30") , ("14:00","20:00")  ]  , #Wednesday
			3: [("9:00","13:30") , ("14:00","20:00")  ] , # Thursday
			4: [("9:00","13:30") , ("14:00","20:00")  ] , # Friday
			5: [("9:00","13:30") , ("14:00","20:00")  ] , # Saturday
			0: [  ], # Sunday
		},
	274 :
		{
			0: [("9:00","13:30") , ("14:00","20:00")  ] , # Monday
			1: [("9:00","13:30") , ("14:00","20:00")  ] , # Tuesday
			2: [("9:00","13:30") , ("14:00","20:00")  ]  , #Wednesday
			3: [  ] , # Thursday
			4: [("9:00","13:30") , ("14:00","20:00")  ] , # Friday
			5: [("9:00","13:30") , ("14:00","20:00")  ] , # Saturday
			0: [  ], # Sunday
		},
}		

''' Gmail Settings, if needed '''
use_gmail = False
gmail_username = 'login'  
gmail_password = 'xxxxxxxx' 

''' SMTP Settings, if not Gmail '''
smtp_relay_server = 'localhost'  # or your isp smtp relay

''' Email settings '''
from_  =  'mymousealarm@mydomain.org'
to_    =  'me@me.org'


''' '''
msg_subject_template = 'My Store - Door %s is %s'
msg_body_template = 'The door %s is %s'

''' '''
''' Don't touch below '''
''' '''

__voidptrsize = ctypes.sizeof(ctypes.c_voidp)
_64bit = (__voidptrsize == 8)
_32bit = (__voidptrsize == 4)
if _64bit:
    INPUTEVENT_STRUCT = "=LLLLHHl"
    INPUTEVENT_STRUCT_SIZE = 24
elif _32bit: # 32bit
    INPUTEVENT_STRUCT = "=iiHHi"
    INPUTEVENT_STRUCT_SIZE = 16
else:
    raise RuntimeError("Couldn't determine architecture, modify " + __file__ +
                       " to support your system.")


def detect_mouse(name):
    fd = file("/proc/bus/input/devices")
    entry = {}
    mouse = None

    for line in fd:
        line = line.strip()
        if not line and entry:
            if entry['N'] == 'Name="'+name+'"':
                ev = None
                ismouse = False
                for h in entry['H'].split(' '):
                    if 'mouse' in h:
                        ismouse = True
                    if 'event' in h:
                        ev = h
                if ismouse:
                    mouse = ev

        elif line:
            l, r = line.split(":", 1)
            r = r.strip()
            entry[l] = r
    return mouse


''' Class from https://github.com/rmt/pyinputevent '''
class InputEvent(object):
    """
    A `struct input_event`. You can instantiate it with a buffer, in which
    case the method `unpack(buf)` will be called.  Or you can create an
    instance with `InputEvent.new(type, code, value, timestamp=None)`, which
    can then be packed into the structure with the `pack` method.
    """
    __slots__ = ('etype', 'ecode', 'evalue', 'time', 'nanotime')

    def __init__(self, buf=None):
        """By default, unpack from a buffer"""
        if buf:
            self.unpack(buf)

    def set(self, etype, ecode, evalue, timestamp=None):
        """Set the parameters of this InputEvent"""
        if timestamp is None:
            timestamp = time.time()
        self.time, self.nanotime = int(timestamp), int(timestamp%1*1000000.0)
        self.etype = etype
        self.ecode = ecode
        self.evalue = evalue
        return self

    @classmethod
    def new(cls, etype, ecode, evalue, time=None):
        """Construct a new InputEvent object"""
        e = cls()
        e.set(etype, ecode, evalue, time)
        return e

    @property
    def timestamp(self):
        return self.time + (self.nanotime / 1000000.0)

    def unpack(self, buf):
        if _64bit:
            self.time, t1, self.nanotime, t3, \
            self.etype, self.ecode, self.evalue \
            = struct.unpack_from(INPUTEVENT_STRUCT, buf)
        elif _32bit:
            self.time, self.nanotime, self.etype, \
            self.ecode, self.evalue \
            = struct.unpack_from(INPUTEVENT_STRUCT, buf)
        return self
    def pack(self):
        if _64bit:
            return struct.pack(INPUTEVENT_STRUCT, 
            self.time, 0, self.nanotime, 0,
            self.etype, self.ecode, self.evalue)
        elif _32bit:
            return struct.pack(INPUTEVENT_STRUCT, 
            self.time, self.nanotime,
            self.etype, self.ecode, self.evalue)
    def __repr__(self):
        return "<InputEvent type=%r, code=%r, value=%r>" % \
            (self.etype, self.ecode, self.evalue)
    def __str__(self):
        return "type=%r, code=%r, value=%r" % \
            (self.etype, self.ecode, self.evalue)
    def __hash__(self):
        return hash( (self.etype, self.ecode, self.evalue,) )
    def __eq__(self, other):
        return self.etype == other.etype \
            and self.ecode == other.ecode \
            and self.evalue == other.evalue




def receive(event):
    global use_gmail, from_, to_
    if event.etype == 1:
    	msg_subject =  msg_subject_template  % (mouse_key_dic[event.ecode] , mouse_event_dic[event.evalue] )
    	msg_body =  msg_body_template  % (mouse_key_dic[event.ecode] , mouse_event_dic[event.evalue] )  
    	
    	if check_days_off(event.ecode):
    		print time.strftime("[%b %d %Y %H:%M:%S] ") +   msg_subject
    		send_email(from_,to_,msg_subject ,msg_body, use_gmail )
    sys.stdout.flush()


def check_days_off(key):
    locale.setlocale(locale.LC_ALL,'')
    current_date = date.today()
    current_time = time.strptime( time.strftime("%H:%M"), '%H:%M')
   
    
    if any(time.strptime(lower, '%H:%M') <= current_time <= time.strptime(upper, '%H:%M') for (lower, upper) in days_off_dic[key][current_date.weekday()] ):
    	days_off = False
    else:
    	days_off = True
    
    return days_off


def send_email(sender, receiver, subject, body, use_gmail=False):
    global gmail_username, gmail_password, smtp_relay_server 
    msg = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" 
        %(sender, receiver, subject, body))

    if use_gmail == True:
      s = smtplib.SMTP('smtp.gmail.com:587')
      s.starttls()  
      s.login(gmail_username,gmail_password)  
    else:
      s = smtplib.SMTP(smtp_relay_server )

    s.sendmail(sender, [receiver], msg)
    s.quit()

if __name__ == '__main__':
	''' '''
	event_name = detect_mouse(device_name)
	 
	if event_name != None: 
		device = "/dev/input/" + event_name
		print "Mouse found ! Use event file %s" % device
		''' Disable mouse from X11 '''
	 	os.system('xinput set-int-prop '+ xinput_id + ' "Device Enabled" 8 0' )
	
		''' '''
		fileno = os.open(device, os.O_RDONLY)
		buf = ""
		"""
		Read up to 4096 bytes from the input device, and generate an
		InputEvent for every 24 (or 16) bytes (sizeof(struct input_event))
		"""
		while True:
			buf += os.read(fileno, 4096)
			while len(buf) >= INPUTEVENT_STRUCT_SIZE:
				receive(InputEvent(buf[:INPUTEVENT_STRUCT_SIZE]))
				buf = buf[INPUTEVENT_STRUCT_SIZE:]

	else:
		print "Unable to found define mouse. Bye."
