From 31f156d2e8f618fa10bdee93832320747a5e8c1d Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Tue, 17 Dec 2024 23:33:22 +0000 Subject: [PATCH 01/17] feat: implement lock-out functionality to door controller --- app/API/index.py | 12 +++++++++++- app/Classes/DoorControl.py | 21 ++++++++++++++++++--- app/Classes/RFID_Reader.py | 4 +--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/API/index.py b/app/API/index.py index e454fe8..9b8d437 100644 --- a/app/API/index.py +++ b/app/API/index.py @@ -1,9 +1,10 @@ import logging -from flask import Flask, jsonify +from flask import Flask, jsonify, request from .routes import accessLog, camera, card, user import os import multiprocessing +from app.classes.DoorControl import door_controller os.environ["FLASK_RUN_FROM_CLI"] = "false" @@ -63,3 +64,12 @@ def destroy_api(): @app.route("/") def hello_world(): return jsonify("Hello World"), 200 + +@app.route("/lockout", methods=["POST"]) +def set_lockout(): + data = request.get_json() + + should_lockout = data.get("should_lockout") == "true" + door_controller.set_lockout(should_lockout) + + return jsonify({{"locked_out": str(should_lockout)}}), 200 \ No newline at end of file diff --git a/app/Classes/DoorControl.py b/app/Classes/DoorControl.py index e6e5a5a..093f034 100644 --- a/app/Classes/DoorControl.py +++ b/app/Classes/DoorControl.py @@ -3,36 +3,51 @@ from enum import Enum sysfs_path = "/sys/kernel/led_toggle/led_toggle" - class DoorState(Enum): LOCKED = 0, # Light Off [LOW] UNLOCKED = 1 # Light On [HIGH] class DoorControl: def __init__(self): + self.locked_out = False if not path.exists(sysfs_path): raise FileNotFoundError(f"Sysfs File Not Found. Expected Location: {sysfs_path}") def get_state(self): with open(sysfs_path, "r") as file: state = file.read().strip() - return int(state) + return [ + state, + self.locked_out + ] def lock(self): + if self.locked_out: + return False self._write_state(DoorState.LOCKED) def unlock(self, seconds: int): + if self.locked_out: + return False self._write_state(DoorState.UNLOCKED) if seconds: sleep(seconds) self.lock() def toggle_lock(self): + if self.locked_out: + return False + if self.get_state() == DoorState.LOCKED: self.unlock() else: self.lock() + def set_lockout(self, shouldLockout:bool): + self.locked_out = shouldLockout + def _write_state(self, state:int): with open(sysfs_path, "w") as file: - file.write(str(state)) \ No newline at end of file + file.write(str(state)) + +door_controller = DoorControl() \ No newline at end of file diff --git a/app/Classes/RFID_Reader.py b/app/Classes/RFID_Reader.py index 5213b82..06c7d17 100644 --- a/app/Classes/RFID_Reader.py +++ b/app/Classes/RFID_Reader.py @@ -3,12 +3,10 @@ import multiprocessing from aws import db import util.general -from app.classes.DoorControl import DoorControl +from app.classes.DoorControl import door_controller sys.path.append("..") -door_controller = DoorControl() - class RFID_Reader: def __init__(self, logger, camera): self.status = False From c10708fcb79d79601a559ad0a8acd513af797569 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Tue, 17 Dec 2024 23:33:52 +0000 Subject: [PATCH 02/17] feat: implement lock-out functionality to door controller Resolves #14 --- app/Classes/DoorControl.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/Classes/DoorControl.py b/app/Classes/DoorControl.py index 093f034..5eb6f89 100644 --- a/app/Classes/DoorControl.py +++ b/app/Classes/DoorControl.py @@ -3,23 +3,25 @@ from enum import Enum sysfs_path = "/sys/kernel/led_toggle/led_toggle" + + class DoorState(Enum): - LOCKED = 0, # Light Off [LOW] - UNLOCKED = 1 # Light On [HIGH] + LOCKED = (0,) # Light Off [LOW] + UNLOCKED = 1 # Light On [HIGH] + class DoorControl: def __init__(self): self.locked_out = False if not path.exists(sysfs_path): - raise FileNotFoundError(f"Sysfs File Not Found. Expected Location: {sysfs_path}") + raise FileNotFoundError( + f"Sysfs File Not Found. Expected Location: {sysfs_path}" + ) def get_state(self): with open(sysfs_path, "r") as file: state = file.read().strip() - return [ - state, - self.locked_out - ] + return [state, self.locked_out] def lock(self): if self.locked_out: @@ -37,17 +39,18 @@ def unlock(self, seconds: int): def toggle_lock(self): if self.locked_out: return False - + if self.get_state() == DoorState.LOCKED: self.unlock() else: self.lock() - def set_lockout(self, shouldLockout:bool): + def set_lockout(self, shouldLockout: bool): self.locked_out = shouldLockout - def _write_state(self, state:int): + def _write_state(self, state: int): with open(sysfs_path, "w") as file: file.write(str(state)) -door_controller = DoorControl() \ No newline at end of file + +door_controller = DoorControl() From 980bb5e04c2e747272f859b31ccfedcf0752ea3c Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 13:34:25 +0000 Subject: [PATCH 03/17] fix: reference to doorControl class --- app/api/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/index.py b/app/api/index.py index f73a310..e521f92 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -4,7 +4,7 @@ from .routes import accessLog, camera, card, user import os import multiprocessing -from app.classes.DoorControl import door_controller +from classes.DoorControl import door_controller os.environ["FLASK_RUN_FROM_CLI"] = "false" From 46b206ff000eade806ab0f736f8269c4eb492d43 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 13:35:29 +0000 Subject: [PATCH 04/17] fix: RFID reader import --- app/api/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/index.py b/app/api/index.py index e521f92..dc25154 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -4,7 +4,7 @@ from .routes import accessLog, camera, card, user import os import multiprocessing -from classes.DoorControl import door_controller +from classes.RFID_Reader import door_controller os.environ["FLASK_RUN_FROM_CLI"] = "false" From d48a72d11c1d24fb92fb8107a24c46e78a4143c8 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 13:37:03 +0000 Subject: [PATCH 05/17] chore: update typings for should_lockout --- app/api/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/index.py b/app/api/index.py index dc25154..6c75b95 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -71,7 +71,7 @@ def hello_world(): def set_lockout(): data = request.get_json() - should_lockout = data.get("should_lockout") == "true" + should_lockout = str(data.get("should_lockout")).lower() == "true" door_controller.set_lockout(should_lockout) return jsonify({{"locked_out": str(should_lockout)}}), 200 \ No newline at end of file From 3bb3b4cd5559309846f4f4eee524c3420c1a94d4 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 13:42:27 +0000 Subject: [PATCH 06/17] chore: add clean up to module load --- kernel/load_module.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/load_module.sh b/kernel/load_module.sh index 39de631..29ea771 100644 --- a/kernel/load_module.sh +++ b/kernel/load_module.sh @@ -1,5 +1,8 @@ #!/bin/bash # Compile LKM +echo "Cleaning up......" +make clean + echo "Compiling Linux Kernel Module (led_toggle)..." make From 6bf4473c644a53d125e62f92fb58dd52c93ed019 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 13:46:21 +0000 Subject: [PATCH 07/17] chore: ensure sudo permissions on startup --- startup.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/startup.sh b/startup.sh index 99206cb..bfef570 100644 --- a/startup.sh +++ b/startup.sh @@ -1,4 +1,9 @@ #!/bin/bash +if [ "$(id -u)" -ne 0 ]; then + echo "This script must executed as root (OR with the sudo command)" + exit 1 +fi + echo "Setting up Linux Kernel Module..." cd kernel/ sudo ./load_module.sh @@ -7,4 +12,4 @@ cd .. echo "Setting up Python Application..." cd app -./load_python.sh \ No newline at end of file +sudo ./load_python.sh \ No newline at end of file From 25acef3536575f362a4c7c5c84dd86307d2659a6 Mon Sep 17 00:00:00 2001 From: Liam Townsley <88986614+LiamTownsley2@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:51:16 +0000 Subject: [PATCH 08/17] chore: add execute permission to *.sh files --- app/load_python.sh | 0 kernel/load_module.sh | 0 startup.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 app/load_python.sh mode change 100644 => 100755 kernel/load_module.sh mode change 100644 => 100755 startup.sh diff --git a/app/load_python.sh b/app/load_python.sh old mode 100644 new mode 100755 diff --git a/kernel/load_module.sh b/kernel/load_module.sh old mode 100644 new mode 100755 diff --git a/startup.sh b/startup.sh old mode 100644 new mode 100755 From 76aaed1c6567d1b6d72678552d3e659c9a7c1bea Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 13:54:03 +0000 Subject: [PATCH 09/17] chore: remove unused requirement python-dotenv --- app/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/requirements.txt b/app/requirements.txt index 728d623..746543c 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -1,5 +1,4 @@ boto3 -python-dotenv picamera smbus2 mfrc522 From d52aadbdd6e49f4ec7d25449e90cebdad4225275 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:03:25 +0000 Subject: [PATCH 10/17] fix: add region to s3 --- app/aws/S3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/aws/S3.py b/app/aws/S3.py index 4069bbf..34e9554 100644 --- a/app/aws/S3.py +++ b/app/aws/S3.py @@ -6,6 +6,7 @@ aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), aws_session_token=os.getenv("AWS_SESSION_TOKEN"), + region_name=os.getenv("AWS_REGION") ) From f52fd8a5221074eb221530113727a5d5dc3a4246 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:19:56 +0000 Subject: [PATCH 11/17] fix: add exception handling to /lockout --- app/api/index.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/api/index.py b/app/api/index.py index 6c75b95..8eba210 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -5,6 +5,7 @@ import os import multiprocessing from classes.RFID_Reader import door_controller +import traceback os.environ["FLASK_RUN_FROM_CLI"] = "false" @@ -69,9 +70,12 @@ def hello_world(): @app.route("/lockout", methods=["POST"]) def set_lockout(): - data = request.get_json() + try: + data = request.get_json() - should_lockout = str(data.get("should_lockout")).lower() == "true" - door_controller.set_lockout(should_lockout) + should_lockout = str(data.get("should_lockout")).lower() == "true" + door_controller.set_lockout(should_lockout) - return jsonify({{"locked_out": str(should_lockout)}}), 200 \ No newline at end of file + return jsonify({{"locked_out": str(should_lockout)}}), 200 + except Exception as e: + return jsonify({"error": e, "stack": traceback.format_exc()}), 400 \ No newline at end of file From b05ba5ca3744923549ba7aaf2bbaab390c4f292d Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:27:28 +0000 Subject: [PATCH 12/17] fix lockout --- app/api/index.py | 10 ++++------ app/classes/DoorControl.py | 8 +++++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/api/index.py b/app/api/index.py index 8eba210..a85b48e 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -1,6 +1,6 @@ import logging -from flask import Flask, jsonify, request +from flask import Flask, jsonify from .routes import accessLog, camera, card, user import os import multiprocessing @@ -71,11 +71,9 @@ def hello_world(): @app.route("/lockout", methods=["POST"]) def set_lockout(): try: - data = request.get_json() - - should_lockout = str(data.get("should_lockout")).lower() == "true" - door_controller.set_lockout(should_lockout) + state = door_controller.get_state() + door_controller.set_lockout(not state) - return jsonify({{"locked_out": str(should_lockout)}}), 200 + return jsonify({{"locked_out": str(not state)}}), 200 except Exception as e: return jsonify({"error": e, "stack": traceback.format_exc()}), 400 \ No newline at end of file diff --git a/app/classes/DoorControl.py b/app/classes/DoorControl.py index c962db2..5ce9c49 100644 --- a/app/classes/DoorControl.py +++ b/app/classes/DoorControl.py @@ -24,16 +24,19 @@ def __init__(self): def get_state(self): with open(sysfs_path, "r") as file: state = file.read().strip() - return [state, self.locked_out] + is_active = state == "1" + return [is_active, self.locked_out] def lock(self): if self.locked_out: return False + self._write_state(DoorState.LOCKED) def unlock(self, seconds: int): if self.locked_out: return False + self._write_state(DoorState.UNLOCKED) if seconds: sleep(seconds) @@ -48,6 +51,9 @@ def toggle_lock(self): else: self.lock() + def set_lockout(self, shouldLockout: bool): + self.locked_out = shouldLockout + def _write_state(self, state: DoorState): thread_logger.info(f"Attempting to write state {str(state.value)}") From 8997987d7d1d8e3f0815a1c08d7fcfe26172b590 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:32:18 +0000 Subject: [PATCH 13/17] fix: locked_out route --- app/api/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/api/index.py b/app/api/index.py index a85b48e..d01da2b 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -74,6 +74,6 @@ def set_lockout(): state = door_controller.get_state() door_controller.set_lockout(not state) - return jsonify({{"locked_out": str(not state)}}), 200 + return jsonify({"locked_out": str(not state)}), 200 except Exception as e: - return jsonify({"error": e, "stack": traceback.format_exc()}), 400 \ No newline at end of file + return jsonify({"error": str(e), "stack": traceback.format_exc()}), 400 \ No newline at end of file From 213d8ca4b729a89379b4a66f5c765ff2e951f361 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:34:02 +0000 Subject: [PATCH 14/17] fix: reference to get_state() --- app/api/index.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/api/index.py b/app/api/index.py index d01da2b..24ec34f 100644 --- a/app/api/index.py +++ b/app/api/index.py @@ -71,9 +71,9 @@ def hello_world(): @app.route("/lockout", methods=["POST"]) def set_lockout(): try: - state = door_controller.get_state() - door_controller.set_lockout(not state) + _, locked_out_state = door_controller.get_state() + door_controller.set_lockout(not locked_out_state) - return jsonify({"locked_out": str(not state)}), 200 + return jsonify({"locked_out": str(not locked_out_state)}), 200 except Exception as e: return jsonify({"error": str(e), "stack": traceback.format_exc()}), 400 \ No newline at end of file From 52941d2b14dc3da95eca23bc5b0436572de6e5aa Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:38:22 +0000 Subject: [PATCH 15/17] refactor: update _Startreader --- app/classes/RFID_Reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/classes/RFID_Reader.py b/app/classes/RFID_Reader.py index 32acd92..e1ce47b 100644 --- a/app/classes/RFID_Reader.py +++ b/app/classes/RFID_Reader.py @@ -82,7 +82,7 @@ def _start_reader(self): f"*{'VALID' if is_valid else 'INVALID'} TAG READ* | ID: {id} | Text: '{text}'" ) - if is_valid: + if is_valid and not door_controller.get_state()[1]: door_controller.unlock(3) self.logger.info("Attempting upload to bucket.") bucket, file_object = self.camera.record_and_upload( From d47ebf36534458c480d7d315907e668c6f35f943 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:40:28 +0000 Subject: [PATCH 16/17] feat: add .get_locked_out() function --- app/classes/DoorControl.py | 4 +++- app/classes/RFID_Reader.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/classes/DoorControl.py b/app/classes/DoorControl.py index 5ce9c49..8525e4f 100644 --- a/app/classes/DoorControl.py +++ b/app/classes/DoorControl.py @@ -26,7 +26,9 @@ def get_state(self): state = file.read().strip() is_active = state == "1" return [is_active, self.locked_out] - + def get_locked_out(self): + return self.locked_out + def lock(self): if self.locked_out: return False diff --git a/app/classes/RFID_Reader.py b/app/classes/RFID_Reader.py index e1ce47b..32840db 100644 --- a/app/classes/RFID_Reader.py +++ b/app/classes/RFID_Reader.py @@ -82,7 +82,7 @@ def _start_reader(self): f"*{'VALID' if is_valid else 'INVALID'} TAG READ* | ID: {id} | Text: '{text}'" ) - if is_valid and not door_controller.get_state()[1]: + if is_valid and not door_controller.get_locked_out(): door_controller.unlock(3) self.logger.info("Attempting upload to bucket.") bucket, file_object = self.camera.record_and_upload( From 6a03aaabc14592484231ddfa8cab3c101e2123f2 Mon Sep 17 00:00:00 2001 From: Liam Townsley Date: Fri, 20 Dec 2024 14:50:52 +0000 Subject: [PATCH 17/17] fix: toggle_lock --- app/classes/DoorControl.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/classes/DoorControl.py b/app/classes/DoorControl.py index 8525e4f..5c86c0b 100644 --- a/app/classes/DoorControl.py +++ b/app/classes/DoorControl.py @@ -25,7 +25,7 @@ def get_state(self): with open(sysfs_path, "r") as file: state = file.read().strip() is_active = state == "1" - return [is_active, self.locked_out] + return is_active, self.locked_out def get_locked_out(self): return self.locked_out @@ -45,10 +45,11 @@ def unlock(self, seconds: int): self.lock() def toggle_lock(self): - if self.locked_out: + state, locked_out = self.get_state() + if locked_out: return False - - if self.get_state() == DoorState.LOCKED: + + if state: self.unlock() else: self.lock()