Skip to content

Commit

Permalink
chore: Update .gitignore and package-lock.json, add system monitor co…
Browse files Browse the repository at this point in the history
…mponent, and update settings functionality
  • Loading branch information
infinitel8p committed Aug 21, 2024
1 parent 5e6daab commit f4e3974
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 32 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode/
.env
dev
node_modules
node_modules
__pycache__
20 changes: 19 additions & 1 deletion client/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from flask import Flask, Response, jsonify
from flask import Flask, Response, jsonify, request
from flask_cors import CORS
import system_helpers
import stream_helpers
import settings_helpers

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
Expand Down Expand Up @@ -36,6 +37,23 @@ def toggle_recording():
else:
stream_helpers.start_recording()
return jsonify({"message": "Recording started"})

@app.route('/settings', methods=['GET', 'POST'])
def settings():
if request.method == 'GET':
return jsonify(settings_helpers.get_settings())
elif request.method == 'POST':
new_settings = request.json
if "VideoSaveLocation" in new_settings:
success = settings_helpers.update_video_save_location(new_settings["VideoSaveLocation"])
if success:
return jsonify({"message": "Settings updated"})
else:
return jsonify({"message": "Invalid directory or insufficient permissions"}), 400
else:
settings_helpers.update_settings(new_settings)
return jsonify({"message": "Settings updated"})



if __name__ == "__main__":
Expand Down
23 changes: 23 additions & 0 deletions client/settings/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"TARGET_BT_ADDRESSES": [
{
"address": "XX:XX:XX:XX:XX:XX",
"name": "Device 1"
},
{
"address": "XX:XX:XX:XX:XX:XX",
"name": "Device 2"
}
],
"TARGET_AP_MAC_ADDRESSES": [
{
"address": "XX:XX:XX:XX:XX:XX",
"name": "Device 1"
},
{
"address": "XX:XX:XX:XX:XX:XX",
"name": "Device 2"
}
],
"VideoSaveLocation": "./recordings"
}
37 changes: 37 additions & 0 deletions client/settings_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import json
import os

SETTINGS_FILE = './settings/settings.json'

def get_settings():
with open(SETTINGS_FILE, 'r') as f:
settings = json.load(f)
return settings

def update_settings(new_settings):
with open(SETTINGS_FILE, 'r+') as f:
settings = json.load(f)
settings.update(new_settings)
f.seek(0)
json.dump(settings, f, indent=4)
f.truncate()

def is_valid_directory(path):
if not os.path.exists(path):
try:
os.makedirs(path)
return True
except Exception as e:
print(f"Error creating directory: {e}")
return False
elif os.path.isdir(path) and os.access(path, os.W_OK):
return True
else:
return False

def update_video_save_location(new_location):
if is_valid_directory(new_location):
update_settings({"VideoSaveLocation": new_location})
return True
else:
return False
15 changes: 11 additions & 4 deletions client/stream_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import cv2
import json
import threading
from datetime import datetime

Expand All @@ -8,8 +9,8 @@
out = None
is_recording = False

# Ensure recordings directory exists
os.makedirs('./recordings', exist_ok=True)
# Load settings.json
SETTINGS_FILE = './settings/settings.json'


def generate_frames():
Expand Down Expand Up @@ -51,10 +52,16 @@ def start_recording() -> None:
Returns:
None
"""

global out, is_recording

with open(SETTINGS_FILE, 'r') as f:
settings = json.load(f)

video_save_location = settings.get('VideoSaveLocation', './recordings')
os.makedirs(video_save_location, exist_ok=True)

timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f'./recordings/output_{timestamp}.avi'
filename = os.path.join(video_save_location, f'output_{timestamp}.avi')

fourcc = cv2.VideoWriter_fourcc(*'XVID')
with lock:
Expand Down
32 changes: 19 additions & 13 deletions client/system_helpers.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,60 @@
import psutil


def get_cpu_temp() -> float | None:
def get_cpu_temp() -> int | None:
"""
Returns the CPU temperature of the Raspberry Pi.
Returns:
float: The CPU temperature in Celsius.
int: The CPU temperature in Celsius rounded to the nearest integer.
None: If the temperature file is not found.
"""

try:
with open("/sys/class/thermal/thermal_zone0/temp", "r") as file:
temp_str = file.read().strip()
cpu_temp = int(temp_str) / 1000.0
return cpu_temp
return round(cpu_temp)
except FileNotFoundError:
return None


def get_cpu_load() -> float:
def get_cpu_load() -> int:
"""
Returns the CPU load as a percentage.
Returns the CPU load as a percentage, considering the maximum load across all cores.
Returns:
float: The CPU load as a percentage.
int: The CPU load as a percentage rounded to the nearest integer.
"""
return psutil.cpu_percent(interval=1)

per_core_loads = psutil.cpu_percent(interval=1, percpu=True)
max_load = max(per_core_loads)
return round(max_load)


def get_storage_info() -> dict[str, float]:
def get_storage_info() -> dict[str, int]:
"""
Returns the total size and used space of the disk where the root directory is mounted.
Returns:
dict[str, float]: A dictionary containing the total and used space in GB.
dict[str, int]: A dictionary containing the total and used space in GB, each rounded to the nearest integer.
"""

usage = psutil.disk_usage('/')
total = usage.total / (1024 ** 3) # Convert bytes to GB
used = usage.used / (1024 ** 3) # Convert bytes to GB
return {'total_gb': total, 'used_gb': used}
return {'total_gb': round(total), 'used_gb': round(used)}


def get_ram_usage() -> dict[str, float]:
def get_ram_usage() -> dict[str, int]:
"""
Returns the total and used RAM in MB.
Returns:
dict[str, float]: A dictionary containing the total and used RAM in MB.
dict[str, int]: A dictionary containing the total and used RAM in MB, each rounded to the nearest integer.
"""

mem = psutil.virtual_memory()
total = mem.total / (1024 ** 2) # Convert bytes to MB
used = mem.used / (1024 ** 2) # Convert bytes to MB
return {'total_mb': total, 'used_mb': used}
return {'total_mb': round(total), 'used_mb': round(used)}
11 changes: 9 additions & 2 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions server/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import ClientVideoPlayer from "@/components/ClientVideoPlayer";
import SystemMonitor from "@/components/SystemMonitor";

const Home = () => {

Expand All @@ -25,11 +26,7 @@ const Home = () => {
</div>
</div>

<div className="grid grid-cols-3 border border-red-200 gap-10">
<div className="border border-red-500">temp: 20°C</div>
<div className="border border-red-500">cpu: 20%</div>
<div className="border border-red-500">storage: 2.69gb / 64gb</div>
</div>
<SystemMonitor />
</div>
);
};
Expand Down
76 changes: 70 additions & 6 deletions server/src/app/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,76 @@
const page = () => {
'use client';
import React, { useState, useEffect } from "react";

const Page = () => {
const [settings, setSettings] = useState({
VideoSaveLocation: "Loading...",
});
const [newLocation, setNewLocation] = useState("");

useEffect(() => {
const settingsFeedUrl = `${window.location.protocol}//${window.location.hostname}:5005/settings`;

const fetchSettingsInfo = async () => {
try {
const response = await fetch(settingsFeedUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log("Fetched Settings Info:", data);
setSettings(data);
} catch (error) {
console.error("Error fetching settings info:", error);
setSettings({
VideoSaveLocation: "Error loading data",
});
}
};

fetchSettingsInfo();
}, []);

const handleSaveLocationChange = async () => {
const settingsFeedUrl = `${window.location.protocol}//${window.location.hostname}:5005/settings`;
try {
const response = await fetch(settingsFeedUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ VideoSaveLocation: newLocation }),
});

const result = await response.json();
if (!response.ok) {
alert(result.message || "Error updating settings");
} else {
alert("Settings updated successfully");
setSettings(prevSettings => ({ ...prevSettings, VideoSaveLocation: newLocation }));
}
} catch (error) {
console.error("Error updating settings:", error);
alert("Error updating settings");
}
};

return (
<div>
<div>bt device mac</div>
<div>wifi device mac</div>
<div>modular trigger sensors</div>
<div>video save location</div>
<div>{`Video save location: ${settings.VideoSaveLocation}`}</div>
<div>
<input
type="text"
value={newLocation}
onChange={(e) => setNewLocation(e.target.value)}
placeholder="Enter new video save location"
/>
<button onClick={handleSaveLocationChange}>Update Location</button>
</div>
<div className="text-gray-300">modular trigger sensors: TBD</div>
</div>
)
}
);
};

export default page
export default Page;
43 changes: 43 additions & 0 deletions server/src/components/SystemMonitor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';
import React, { useState, useEffect } from "react";

const SystemMonitor = () => {
const [systemInfo, setSystemInfo] = useState({
cpu_temp_celsius: "Loading...",
cpu_load_percent: "Loading...",
storage_info_gb: { total_gb: "Loading...", used_gb: "Loading..." },
ram_usage_mb: { total_mb: "Loading...", used_mb: "Loading..." },
});

const systemFeedUrl = `${window.location.protocol}//${window.location.hostname}:5005/system_info`;

useEffect(() => {
const fetchSystemInfo = async () => {
try {
const response = await fetch(systemFeedUrl);
const data = await response.json();
console.log("Fetched System Info:", data); // Log the data structure
setSystemInfo(data);
} catch (error) {
console.error("Error fetching system info:", error);
}
};

fetchSystemInfo();
}, [systemFeedUrl]);

return (
<div className="grid grid-cols-3 border border-red-200 gap-10">
<div className="border border-red-500 text-center">Temp: {systemInfo.cpu_temp_celsius}°C</div>
<div className="border border-red-500 text-center">CPU: {systemInfo.cpu_load_percent}%</div>
<div className="border border-red-500 text-center">
Storage: {systemInfo.storage_info_gb.used_gb} GB / {systemInfo.storage_info_gb.total_gb} GB
</div>
<div className="border border-red-500 text-center">
RAM: {systemInfo.ram_usage_mb.used_mb} MB / {systemInfo.ram_usage_mb.total_mb} MB
</div>
</div>
);
};

export default SystemMonitor;

0 comments on commit f4e3974

Please sign in to comment.