def _handle_sensor_device_state_change_event(self, event): body = event.body device_name = body[Evts.SensorDeviceBodyItem.DeviceName] state = body[Evts.SensorDeviceBodyItem.State] triggered = bool(state == 1) state_str = "opened" if triggered else "closed" # If the alarm is deactived then ignore the sensor state change after # logging the change for reference. if self._current_alarm_state == self.AlarmState.Deactivated: log_msg = f"{device_name} was {state_str}, although alarm isn't on" self._logger.Log(LogType.Info, log_msg) return # If the trigger has has already been triggered then opening or closing # a door etc. would change the alarm state, although we should log that # the even occurred. if self._current_alarm_state == self.AlarmState.Triggered: log_msg = f"{device_name} was {state_str}, alarm already triggered" self._logger.Log(LogType.Info, log_msg) return if self._current_alarm_state == self.AlarmState.Activated: log_msg = f"Activity on {device_name} ({state_str}) has triggerd " +\ "the alarm!" self._logger.Log(LogType.Info, log_msg) self._current_alarm_state = self.AlarmState.Triggered evt = Event(Evts.EvtType.ActivateSiren, None) self._event_mgr.QueueEvent(evt)
def _generate_device_state_change_evt(self): evt_body = { Evts.SensorDeviceBodyItem.DeviceType: self.SensorName, Evts.SensorDeviceBodyItem.DeviceName: self._device_name, Evts.SensorDeviceBodyItem.State: self._is_triggered } evt = Event(Evts.EvtType.SensorDeviceStateChange, evt_body) self._event_mgr.QueueEvent(evt)
def _trigger_alarm(self, no_grace_time=False): self._current_alarm_state = self.AlarmState.Activated alarm_set_evt_body = { 'activationTimestamp': time.time(), 'noGraceTime': no_grace_time } activate_event = Event(Evts.EvtType.AlarmActivated, alarm_set_evt_body) self._event_mgr.QueueEvent(activate_event)
def _please_respond_to_keypad(self): # Validate the request to ensure that the auth key is firstly present, # then if it's valid. None is returned if successful. validate_return = self._validate_auth_key() if validate_return is not None: return validate_return send_alive_ping_evt = Event(Evts.EvtType.KeypadApiSendAlivePing) self._event_mgr.QueueEvent(send_alive_ping_evt) return self._endpoint.response_class(response='Ok', status=HTTPStatusCode.OK, mimetype=MIMEType.Text)
def _receive_key_code(self): # Check for that the message body ia of type application/json and that # there is one, if not report a 400 error status with a human-readable. body = request.get_json() if not body: err_msg = 'Missing/invalid json body' response = self._endpoint.response_class( response=err_msg, status=HTTPStatusCode.BadRequest, mimetype=MIMEType.Text) return response # Validate the request to ensure that the auth key is firstly present, # then if it's valid. None is returned if successful. validate_return = self._validate_auth_key() if validate_return is not None: return validate_return # Validate that the json body conforms to the expected schema. # If the message isn't valid then a 400 error should be generated. try: jsonschema.validate(instance=body, schema=schemas.ReceiveKeyCode.Schema) except jsonschema.exceptions.ValidationError: err_msg = 'Message body validation failed.' return self._endpoint.response_class( response=err_msg, status=HTTPStatusCode.BadRequest, mimetype='text') evt = Event(Evts.EvtType.KeypadKeyCodeEntered, body) self._event_mgr.QueueEvent(evt) return self._endpoint.response_class(response='Ok', status=HTTPStatusCode.OK, mimetype=MIMEType.Text)
def _deactivate_alarm(self): self._current_alarm_state = self.AlarmState.Deactivated self._failed_entry_attempts = 0 evt = Event(Evts.EvtType.AlarmDeactivated) self._event_mgr.QueueEvent(evt)
def _handle_key_code_entered_event(self, event): body = event.body key_sequence = body[schemas.ReceiveKeyCode.BodyElement.KeySeq] # Read the key code detail from the database. details = self._database.get_keycode_details(key_sequence) if details is not None: if self._current_alarm_state == self.AlarmState.Triggered: self._logger.Log(LogType.Info, 'A triggered alarm has been deactivated') self._current_alarm_state = self.AlarmState.Deactivated self._failed_entry_attempts = 0 evt = Event(Evts.EvtType.DeactivateSiren, None) self._event_mgr.QueueEvent(evt) self._deactivate_alarm() elif self._current_alarm_state == self.AlarmState.Deactivated: self._logger.Log(LogType.Info, 'The alarm has been activated') self._current_alarm_state = self.AlarmState.Activated self._failed_entry_attempts = 0 self._trigger_alarm() elif self._current_alarm_state == self.AlarmState.Activated: self._logger.Log(LogType.Info, 'The alarm has been deactivated') self._current_alarm_state = self.AlarmState.Deactivated self._failed_entry_attempts = 0 self._deactivate_alarm() else: self._logger.Log(LogType.Info, 'An invalid key code was entered on keypad') self._failed_entry_attempts += 1 attempts = self._failed_entry_attempts # If the attempt failed then send the response of type # receiveKeyCodeResponseAction_KeycodeIncorrect along with any # response actions that have been defined in the configuraution # file. if attempts in self._config.failed_attempt_responses: responses = self._config.failed_attempt_responses[attempts] for response in responses: if response == 'disableKeyPad': lock_event_body = { keypadApi.KeypadLockRequest.BodyElement.LockTime: round(time.time()) + int(responses[response]['lockTime']) } lock_event = Event( Evts.EvtType.KeypadApiSendKeypadLock, lock_event_body) self._event_mgr.QueueEvent(lock_event) elif response == 'triggerAlarm': if self._current_alarm_state != self.AlarmState.Triggered: self._logger.Log(LogType.Info, '|=> Alarm has been triggered!') self._trigger_alarm(no_grace_time=True) elif response == 'resetAttemptAccount': self._failed_entry_attempts = 0
def start_app(self): # pylint: disable=too-many-statements self._logger.WriteToConsole = True self._logger.ExternalLogger = self self._logger.Initialise() signal.signal(signal.SIGINT, self._signal_handler) self._logger.Log(LogType.Info, 'Secure Shed Central Controller V%s', VERSION) self._logger.Log(LogType.Info, 'Copyright %s Secure Shed Project Dev Team', COPYRIGHT) self._logger.Log(LogType.Info, 'Licensed under the Apache License, Version 2.0') config_manger = ConfigurationManager() configuration = config_manger.parse_config_file(self._config_file) if not configuration: self._logger.Log(LogType.Error, 'Parse failed, last message : %s', config_manger.last_error_msg) sys.exit(1) self._logger.Log(LogType.Info, '=== Configuration Parameters ===') self._logger.Log(LogType.Info, 'Environment Variables:') self._logger.Log(LogType.Info, '|=> Configuration file : %s', self._config_file) self._logger.Log(LogType.Info, '|=> Database : %s', self.__db) self._logger.Log(LogType.Info, '===================================') self._logger.Log(LogType.Info, '=== Configuration File Settings ===') self._logger.Log(LogType.Info, 'General Settings:') self._logger.Log(LogType.Info, '|=> Devices Config File : %s', configuration.general_settings.devicesConfigFile) self._logger.Log(LogType.Info, '|=> Device Types Config File : %s', configuration.general_settings.deviceTypesConfigFile) self._logger.Log(LogType.Info, 'Keypad Controller Settings:') self._logger.Log(LogType.Info, '|=> Authentication Key : %s', configuration.keypad_controller.authKey) self._logger.Log(LogType.Info, '|=> Endpoint : %s', configuration.keypad_controller.endpoint) self._logger.Log(LogType.Info, 'Central Controller Settings:') self._logger.Log(LogType.Info, '|=> Authentication Key : %s', configuration.central_controller_api.authKey) self._logger.Log(LogType.Info, '|=> Network Port : %s', configuration.central_controller_api.networkPort) self._logger.Log(LogType.Info, '================================') self._event_manager = EventManager() controller_db = ControllerDBInterface() if not controller_db.connect(self.__db): self._logger.Log(LogType.Error, "Database '%s' is missing!", self.__db) sys.exit(1) # Build state manager which manages the state of the alarm itself and # how states are changed due to hardware device(s) being triggered. self._state_mgr = StateManager(controller_db, configuration, self._event_manager, self._logger) # Attempt to load the device types plug-ins, if a plug-in cannot be # found or is invalid then a warning is logged and it's not loaded. device_type_mgr = DeviceTypeManager(self._logger) device_types_cfg = device_type_mgr.read_device_types_config( configuration.general_settings.deviceTypesConfigFile) if not device_types_cfg: self._logger.Log(LogType.Error, device_type_mgr.last_error_msg) sys.exit(1) device_type_mgr.load_device_types() # Load the devices configuration file which contains the devices # attached to the alarm. The devices are matched to the device types # loaded above. devices_cfg = configuration.general_settings.devicesConfigFile devices_cfg_loader = DevicesConfigLoader() self._curr_devices = devices_cfg_loader.read_devices_config_file( devices_cfg) if not self._curr_devices: self._logger.Log(LogType.Error, devices_cfg_loader.last_error_msg) sys.exit(1) self._device_mgr = DeviceManager(device_type_mgr, self._event_manager, self._logger) dev_lst = self._curr_devices[devices_cfg_loader.JsonTopElement.Devices] self._device_mgr.load(dev_lst) self._device_mgr.initialise_hardware() self._register_event_callbacks() # Create the IO processing thread which handles IO requests from # hardware devices. self._worker_thread = WorkerThread(configuration, self._device_mgr, self._event_manager, self._state_mgr, self._logger) self._worker_thread.start() # pylint: disable=unused-variable api_controller = ApiController(self._event_manager, controller_db, configuration, self._endpoint, self._log_store, self._logger) send_alive_ping_evt = Event(Evts.EvtType.KeypadApiSendAlivePing) self._event_manager.QueueEvent(send_alive_ping_evt)