Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ DEBUG=True
BACKEND_PORT=5000
HOST='0.0.0.0'
USE_RELOADER=True
LOG_LEVEL=Info
LOG_LEVEL=Info
TARGET=dev
21 changes: 20 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
# Uses Python 3.9 Slim which do not include all standard Libraries. If any additional standard libraries are needed, they need to be installed separately
FROM python:3.9-slim

ARG TARGETENV=dev

# Installs tools that is needed for building the packages and compile pyhton dependecies
RUN apt-get update && apt-get install -y \
build-essential \
python3-dev \
gcc \
&& rm -rf /var/lib/apt/lists/*

RUN if [ "${TARGETENV}" = "prod" ]; then \
apt-get update && apt-get install -y \
cmake \
python3-opencv \
libgpiod2 \
libgpiod-dev \
libcamera-dev \
v4l-utils \
libopencv-dev \
libv4l-dev \
libcamera-tools \
libavcodec-dev \
libavformat-dev \
libswscale-dev \
&& rm -rf /var/lib/apt/lists/*; \
fi

# Upgrade pip and install dependencies
RUN pip install --upgrade pip
COPY requirements.txt /app/requirements.txt
COPY requirements.${TARGETENV}.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt

# Set the working directory inside the container
Expand Down
4 changes: 4 additions & 0 deletions backend/requirements.dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
flask
flask-cors
python-dotenv
opencv-python
7 changes: 7 additions & 0 deletions backend/requirements.prod.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
flask
flask-cors
gpiod
python-dotenv
opencv-python
dlib
face-recognition
4 changes: 0 additions & 4 deletions backend/requirements.txt

This file was deleted.

9 changes: 9 additions & 0 deletions backend/src/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from flask import Flask
from flask_cors import CORS
from dotenv import load_dotenv
from app.services.camera_service import CameraService

def create_app():
load_dotenv()
Expand All @@ -18,6 +19,13 @@ def create_app():
from app.routes import main_routes
app.register_blueprint(main_routes)

""" camera_service = CameraService()
camera_service.test_camera_feed() """

""" @app.teardown_appcontext
def cleanup(exception=None):
camera_service.release_camera()
"""
return app

def config_logger(app, log_Level='ERROR'):
Expand All @@ -31,3 +39,4 @@ def config_logger(app, log_Level='ERROR'):
console_handler.setFormatter(formatter)

app.logger.addHandler(console_handler)

7 changes: 4 additions & 3 deletions backend/src/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ class Config:
"""
Base configuration Class
"""
DEBUG = os.getenv('DEBUG', 'False') == 'True'
DEBUG = os.getenv('DEBUG', 'False').upper() == 'TRUE'
HOST = os.getenv('HOST', '127.0.0.1')
PORT = os.getenv('BACKEND_PORT')
USE_RELOADER = os.getenv('USE_RELOADER') == 'True'
PORT = os.getenv('BACKEND_PORT', 5000)
USE_RELOADER = os.getenv('USE_RELOADER').upper() == 'TRUE'
LOG_LEVEL = os.getenv('LOG_LEVEL', 'ERROR').upper()
TARGET = os.getenv('TARGET', 'dev')
14 changes: 12 additions & 2 deletions backend/src/app/routes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask import Blueprint
from flask import Blueprint, jsonify, Response
from app.services.gpio_service import turn_led_off, turn_led_on
from app.utils.response_util import create_response

Expand Down Expand Up @@ -34,4 +34,14 @@ def led_off():
turn_led_off()
return create_response("LED ist jetzt aus", "warning")
except Exception as e:
return create_response(f"Error: {str(e)}", "error", 500)
return create_response(f"Error: {str(e)}", "error", 500)

""" @main_routes.route('/capture', methods=['GET'])
def capture_image():
try:
frame = camera_service.capture_frame()
# Encode image to JPEG
_, jpeg = cv2.imencode('.jpg', frame)
return Response(jpeg.tobytes(), mimetype='image/jpeg')
except RuntimeError as e:
return jsonify({'error': str(e)}), 500 """
38 changes: 38 additions & 0 deletions backend/src/app/services/camera_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import cv2

class CameraService:
def __init__(self):
self.cap = cv2.VideoCapture('/dev/video0', cv2.CAP_V4L2)

if not self.cap.isOpened():
raise RuntimeError("Error: Could not open camera.")

self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
self.cap.set(cv2.CAP_PROP_FPS, 30)

print("Camera initialized successfully")

def capture_frame(self):
ret, frame = self.cap.read()
if not ret:
raise RuntimeError("Failed to capture frame from camera")
return frame

def release_camera(self):
self.cap.release()
print("Camera released")

def test_camera_feed(self):
while True:
frame = self.capture_frame()

cv2.imshow("Camera Feed", frame)

if cv2.waitKey(1) & 0xFF == ord('q'):
break

self.release_camera()
cv2.destroyAllWindows()


29 changes: 19 additions & 10 deletions backend/src/app/services/gpio_service.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
try:
# checks if you have access to RPi.GPIO, which is available inside Raspberry PI
import RPi.GPIO as GPIO
except:
# In case of exception, you are executing your script outside of Raspberry PI, so import Mock.GPIO
import logging
from app.config import Config

logger = logging.getLogger(__name__)

if Config.TARGET == 'dev':
logger.info("Development environment detected, using mocked GPIO.")
from mocks import GPIO as GPIO
else:
try:
import gpiod as GPIO
except ImportError as e:
logger.error("Failed to import RPi.GPIO: %s", e)
raise


LED_PIN = 10
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)
""" LED_PIN = 10
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT) """

def turn_led_on():
GPIO.output(LED_PIN, GPIO.HIGH)
""" GPIO.output(LED_PIN, GPIO.HIGH)"""

def turn_led_off():
GPIO.output(LED_PIN, GPIO.LOW)
""" GPIO.output(LED_PIN, GPIO.LOW) """
54 changes: 44 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,48 @@ services:
build:
context: ./backend
dockerfile: Dockerfile
args:
TARGETENV: ${TARGET:-dev}
ports:
- "${BACKEND_PORT}:${BACKEND_PORT}"
environment:
- LOG_LEVEL=Info
- LOG_LEVEL=${LOG_LEVEL}
volumes:
- ./backend:/app
restart:
always
env_file:
- .env
profiles:
- non-raspberry-pi
networks:
- memory_network

backend_rpi:
build:
context: ./backend
dockerfile: Dockerfile
args:
TARGETENV: ${TARGET:-prod}
ports:
- "${BACKEND_PORT}:${BACKEND_PORT}"
environment:
- LOG_LEVEL=${LOG_LEVEL}
volumes:
- ./backend:/app
env_file:
- .env
devices:
- /dev/gpiochip0:/dev/gpiochip0
- /dev/video0:/dev/video0
profiles:
- raspberry-pi
networks:
- memory_network
cap_add:
- SYS_ADMIN
- SYS_RAWIO
privileged: true



frontend:
build:
Expand All @@ -27,12 +59,14 @@ services:
- ./frontend:/app
- /app/node_modules
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
- WATCHPACK_POLLING=true
- NODE_ENV=${NODE_ENV}
- CHOKIDAR_USEPOLLING=${CHOKIDAR_USEPOLLING}
- WATCHPACK_POLLING=${WATCHPACK_POLLING}
env_file:
- .env
depends_on:
- backend
restart:
always
networks:
- memory_network

networks:
memory_network:
driver: bridge