class MainThread(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): super(MainThread, self).__init__(group, target, name, args, kwargs, verbose) # load settings file self.cfg = get_settings() # this is used to stop the arming thread self.pill2kill = None # keep a buzzer object to stop it from every function. same for the receiver433 self.buzzer = None # prepare a queue to share data between this threads and other thread self.shared_queue_keyborad = Queue.Queue() self.shared_queue_rfid_reader = Queue.Queue() self.shared_queue_433_receiver = Queue.Queue() self.shared_queue_message_from_api = Queue.Queue() # create an object to manage the screen try: self.screen_manager = AdafruitScreenManager() except IOError: raise Exception("No connection with the screen") # create an object to manage the arduino self.arduino = ArduinoManager() try: self.arduino.ping() except IOError: self.screen_manager.set_arduino_connection_missing() sys.exit(0) # run the keypad thread self.keypad_thread = MatrixKeypadManager(self.shared_queue_keyborad) self.keypad_thread.start() # run the thread that handle RFID rfid = RFIDrc522Manager(self.shared_queue_rfid_reader) rfid.start() # keep 433 receiver thread object in mind self.receiver443 = None # create the flask rest api app = Flask(__name__) flask_api = FlaskAPI(app, self.shared_queue_message_from_api, self) flask_api.start() # Save a buffer where we put each typed number self.code_buffer = "" self.last_state = "disabled" # create the state machine self.fsm = Fysom({'initial': self._get_initial_state(), 'events': [ {'name': 'arm', 'src': 'disabled', 'dst': 'arming'}, {'name': 'enable', 'src': 'arming', 'dst': 'enabled'}, {'name': 'intrusion', 'src': 'enabled', 'dst': 'waiting_code'}, {'name': 'alarm', 'src': 'waiting_code', 'dst': 'alarming'}, {'name': 'disable', 'src': ['arming', 'enabled', 'waiting_code', 'alarming'], 'dst': 'disabled'}], 'callbacks': { 'ondisabled': self.ondisabled, 'onarming': self.onarming, 'onenabled': self.onenabled, 'onwaiting_code': self.onwaiting_code, 'onalarming': self.onalarming} }) def run(self): print "Run main thread" while True: if not self.shared_queue_keyborad.empty(): val = self.shared_queue_keyborad.get() print "Key received from keypad: ", val if val == "D": self.screen_manager.switch_light() elif val == "C": # cancel only if we were in arming status if self.fsm.current == "arming": self.fsm.disable() else: # we add a star to the screen self.screen_manager.add_star() # add the value to the buffer self.code_buffer += str(val) # if we have 4 number we can test the code if len(self.code_buffer) == 4: self._test_pin_code() self.code_buffer = "" if not self.shared_queue_rfid_reader.empty(): val = self.shared_queue_rfid_reader.get() print "Received UID from RFID ", val self._test_rfid_uid(val) if not self.shared_queue_433_receiver.empty(): val = self.shared_queue_433_receiver.get() print "Received UID from 433 receiver ", val self._test_433_uid(val) time.sleep(0.1) def ondisabled(self, e): """ Disable the alarm system :return: """ # stop buzzer if self.buzzer is not None: self.buzzer.stop() # stop arduino counter if it was counting self.arduino.cancel_delayed_siren() # stop siren if it was ringing self.arduino.stop_siren() # if the last status was waiting code if self.last_state == "waiting_code": # then we stop the counter thread self.pill2kill.set() if self.last_state == "arming": self.pill2kill.set() self.screen_manager.cancel_arming() time.sleep(2) # wait 2 seconde with the cancel message before showing disabled status # show disabled on screen self.screen_manager.set_disabled() # we keep the last state in memory self.last_state = "disabled" def onarming(self, e): """ Arming the alarm. Count 20 second. During this time the action can be cancelled by the user :return: """ def doit(stop_event, screen_manager): max_time = self.cfg["arming_time"] while not stop_event.is_set(): for x in range(max_time, 0, -5): if not stop_event.is_set(): screen_manager.lcd.message("%s.." % str(x)) stop_event.wait(5) # counter over, if the user has not cancel, we active the alarm if not stop_event.is_set(): self.fsm.enable() self.screen_manager.reset() self.screen_manager.lcd.message("Arming...\n") self.pill2kill = threading.Event() t = threading.Thread(target=doit, args=(self.pill2kill, self.screen_manager)) t.start() # we start the buzzer self.buzzer = BuzzerManager() self.buzzer.mode = 2 self.buzzer.start() # we keep the last state in memory self.last_state = "arming" def onenabled(self, e): # switch status self.screen_manager.set_enabled() # stop buzzing self.buzzer.stop() # start the 433 receiver thread self.receiver443 = Receiver433Manager(self.shared_queue_433_receiver) self.receiver443.start() # Stop the counter thread self.pill2kill.set() # we keep the last state in memory self.last_state = "enabled" def onwaiting_code(self, e): """ Send a notification to the arduino, this one will sounds the alarm if no code provided :return: """ def doit(stop_event): while not stop_event.is_set(): # wait 20 secondes stop_event.wait(self.cfg["wait_time_before_alarm"]) stop_event.set() self.buzzer.stop() if self.fsm.current == "waiting_code": self.fsm.alarm(location=e.location) # stop the receiver, we do not need it anymore, intrusion already detected self.receiver443.stop() # send notification to the arduino if we are not in notification only mode if not self.cfg["notification_only"]: self.arduino.delayed_siren() # show info on screen self.screen_manager.set_intrustion_detected(e.location) # wait 20 sec the code to disable the alarm self.pill2kill = threading.Event() t = threading.Thread(target=doit, args=(self.pill2kill,)) t.start() # we start the buzzer self.buzzer = BuzzerManager() self.buzzer.mode = 1 self.buzzer.start() # we keep the last state in memory self.last_state = "waiting_code" def onalarming(self, e): """ Switch alarm """ print "Alarm !!" if self.buzzer is not None: self.buzzer.stop() self.screen_manager.set_alarm() # we keep the last state in memory self.last_state = "alarming" try: # send a notification. e.location can be empty if the Rpi has restarted message = "Intrusion: %s" % e.location notify(message) except AttributeError: print "No location in memory" def _test_pin_code(self): """ The user has entered 4 number. We test if typed code is the right one If the code is right, we switch the status of the system If the code is wrong we show a notification on the screen :return: """ print "Valid code is %s" % self.cfg["pin_code"] if str(self.code_buffer) == str(self.cfg["pin_code"]): print "Code valid" self._switch_status() else: print "Code invalid" self.screen_manager.print_invalid_code(self.last_state) def _switch_status(self): """ If we were disabled, switch to enabled, else switch to disabled :return: """ if self.fsm.current == "disabled": # the system was disabled, arming it self.fsm.arm() else: # in all other case, we want to disable self.fsm.disable() def update_status(self, new_alarm_status, new_siren_status): # first, switch the alamr status if new_alarm_status == "enabled": self.fsm.enable() else: self.fsm.disable() if new_siren_status == "on": print "Start the siren" self.arduino.start_siren() else: print "Stop the siren" self.arduino.stop_siren() def _test_rfid_uid(self, uid): # one little bip to notify the user that we scanned the card self.buzzer = BuzzerManager() self.buzzer.mode = 3 self.buzzer.start() time.sleep(1) self.buzzer.stop() if uid in self.cfg['rfid']['valid_uid']: print "Valid UID" self._switch_status() else: print "Invalid UID" self.screen_manager.print_invalid_card(self.last_state) def _test_433_uid(self, uid): """ Test the received uid against settings uid. Should be always a valid code :param uid: received ID from the 433MHZ receiver sent by a sensor :return: """ for el in self.cfg['sensors']: if uid == el['id']: print "Valid sensor ID from %s" % el['location'] # something has been detected self.fsm.intrusion(location=el['location']) # not need to test other ID break else: print "Sensor ID not reconized" def _get_initial_state(self): """ Get the initial state of the system. can be "disabled" if the siren is off or "alarm" if the siren is on :return: """ siren_status = self.arduino.get_siren_status() if siren_status == "off": return "disabled" else: return "alarming"