Enhance project structure and documentation; add new features and templates

- Updated debounce delay in Arduino sketch for improved sensor stability.
- Added comprehensive project structure documentation.
- Expanded future plans with enhanced monitoring and smart features.
- Included Thymeleaf dependencies in pom.xml for template rendering.
- Implemented ArduinoListener with asynchronous processing.
- Created Home controller for dashboard routing.
- Modified SecurityConfig for improved authentication flow.
- Added events.html and home.html templates for user interface.
- Introduced systemSettings.html for configuration management.
- Updated tech stack documentation with detailed descriptions.
This commit is contained in:
Kiyan 2026-01-11 22:35:00 +02:00
parent 1e6449a89d
commit 4955e4a820
11 changed files with 918 additions and 4 deletions

View File

@ -17,7 +17,7 @@ Sensor sensors[] = {
const uint8_t SENSOR_COUNT = sizeof(sensors) / sizeof(sensors[0]); const uint8_t SENSOR_COUNT = sizeof(sensors) / sizeof(sensors[0]);
const unsigned long POLL_INTERVAL_MS = 200; // sampling rate const unsigned long POLL_INTERVAL_MS = 200; // sampling rate
const unsigned long DEBOUNCE_DELAY_MS = 50; const unsigned long DEBOUNCE_DELAY_MS = 200;
const unsigned long HEARTBEAT_MS = 30000; const unsigned long HEARTBEAT_MS = 30000;
unsigned long lastPollTime = 0; unsigned long lastPollTime = 0;

View File

@ -0,0 +1,14 @@
# 📖 Documentation
## Project Structure
The project follows a standard Spring Boot application structure:
- `src/main/java/com/example/HomeSecurity`: Root package.
- `Controllers`: Contains Spring MVC controllers for handling web requests and WebSocket endpoints.
- `Services`: Houses business logic, such as processing sensor data and managing security states.
- `DTOs`: Data Transfer Objects used to shape data for the views and API responses.
- `Components`: Contains hardware integration components, like the `ArduinoListener`.
- `Models`: JPA Entities representing database tables (e.g., `Sensor`).
- `src/main/resources`:
- `static`: For static assets like CSS, JavaScript, and images.
- `templates`: Contains Thymeleaf HTML templates.
- `application.properties`: For application configuration.

View File

@ -0,0 +1,15 @@
# 🚀 Future Plans
We are constantly looking to evolve the Home Security System. Here is what is currently on our development roadmap:
### 📹 Enhanced Monitoring
* **Camera Integration:** Support for IP cameras and RTSP streams to view live feeds alongside sensor data.
* **Floor Plan View:** A visual representation of the home layout with real-time sensor status indicators.
### 🧠 Smart Features
* **AI Anomaly Detection:** Machine learning models to learn routine patterns and alert on unusual activity.
* **Geofencing:** Automatically arm/disarm the system based on the location of the user's mobile device.
### 📱 Connectivity
* **Mobile Application:** A dedicated mobile app for push notifications and easier remote control.
* **Third-party Integration:** Integration with Home Assistant, Google Home, or Alexa.

12
pom.xml
View File

@ -84,6 +84,18 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security-oauth2-client</artifactId> <artifactId>spring-boot-starter-security-oauth2-client</artifactId>

View File

@ -16,6 +16,8 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Component @Component
public class ArduinoListener { public class ArduinoListener {
@ -23,6 +25,8 @@ public class ArduinoListener {
private SerialPort arduinoPort; private SerialPort arduinoPort;
private final StringBuilder buffer = new StringBuilder(); private final StringBuilder buffer = new StringBuilder();
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
@Autowired @Autowired
private SensorService sensorService; private SensorService sensorService;
@ -77,7 +81,8 @@ public class ArduinoListener {
buffer.append(new String(data, StandardCharsets.UTF_8)); buffer.append(new String(data, StandardCharsets.UTF_8));
processBuffer(); executor.submit(() -> processBuffer());
} }
}); });
} }

View File

@ -0,0 +1,15 @@
package com.example.HomeSecurity.Controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class Home {
@GetMapping("/dashboard")
public String homePage(){
return "home";
}
}

View File

@ -12,12 +12,12 @@ public class SecurityConfig {
public SecurityFilterChain securityFilterChain(HttpSecurity http){ public SecurityFilterChain securityFilterChain(HttpSecurity http){
http http
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers("/login") .requestMatchers("/")
.permitAll() .permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
) )
.oauth2Login(oauth2 -> oauth2 .oauth2Login(oauth2 -> oauth2
.defaultSuccessUrl("/", true) .defaultSuccessUrl("/dashboard", true)
) )
.logout(logout -> logout .logout(logout -> logout
.logoutSuccessUrl("/") .logoutSuccessUrl("/")

View File

@ -0,0 +1,240 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SecureHome | Events & Logs</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<style>
:root {
--bg-color: #f4f6f8;
--navbar-dark: #1e293b;
--transition-speed: 0.3s;
}
body {
background-color: var(--bg-color);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
color: #1e293b;
}
/* --- GLOBAL SHARP UI --- */
.card, .btn, .form-control, .form-select, .badge, .dropdown-menu {
border-radius: 0 !important;
}
.card {
border: 1px solid rgba(0,0,0,0.05);
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
transition: all var(--transition-speed);
}
.navbar {
background-color: var(--navbar-dark) !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
/* --- LOG TABLE STYLING --- */
.table thead th {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 700;
color: #64748b;
background-color: #fcfcfc;
border-bottom: 1px solid #e2e8f0;
padding: 1rem;
}
.table tbody td {
padding: 1rem;
border-bottom: 1px solid #f1f5f9;
}
.timestamp {
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
color: #94a3b8;
}
/* Severity Strip on the left of the row */
.severity-strip {
width: 4px;
padding: 0 !important;
}
/* Live Dot for Connection */
.live-dot {
height: 8px; width: 8px;
background-color: #22c55e;
border-radius: 50% !important; /* Keep icon round */
display: inline-block;
animation: pulse-green 2s infinite;
}
@keyframes pulse-green {
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); }
70% { transform: scale(1); box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); }
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}
.btn-primary { background-color: #0f172a; border-color: #0f172a; }
.btn-primary:hover { background-color: #334155; border-color: #334155; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark mb-4">
<div class="container">
<a class="navbar-brand fw-bold tracking-wide" href="#">
<i class="fas fa-shield-alt me-2 text-info"></i>SECURE<span class="fw-light">HOME</span>
</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto ms-lg-4">
<li class="nav-item"><a class="nav-link" th:href="@{/dashboard}">Dashboard</a></li>
<li class="nav-item"><a class="nav-link" th:href="@{/monitoring}">Monitoring</a></li>
<li class="nav-item"><a class="nav-link active" th:href="@{/events}">Events</a></li>
<li class="nav-item"><a class="nav-link" th:href="@{/settings}">Settings</a></li>
</ul>
<div class="d-flex align-items-center text-light gap-4">
<div id="connection-status" hx-get="/api/system/connection-status" hx-trigger="every 30s" class="d-flex align-items-center small">
<span class="text-success d-flex align-items-center">
<span class="live-dot me-2"></span> System Online
</span>
</div>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="userMenu" data-bs-toggle="dropdown">
<div class="bg-secondary d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
<span class="small">A</span>
</div>
</a>
<ul class="dropdown-menu dropdown-menu-end shadow border-0 mt-2">
<li><a class="dropdown-item" href="#">Profile</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Sign out</a></li>
</ul>
</div>
</div>
</div>
</div>
</nav>
<div class="container pb-5">
<div class="card mb-4 border-0">
<div class="card-body p-4">
<h6 class="text-uppercase text-muted fw-bold mb-3 small tracking-widest">
<i class="fas fa-filter me-2"></i>Filter Events
</h6>
<form class="row g-3 align-items-end">
<div class="col-md-3">
<label class="form-label small fw-bold text-muted text-uppercase">Range</label>
<select class="form-select form-select-sm bg-light border-0">
<option>Last 24 Hours</option>
<option>Last 7 Days</option>
<option>Custom Range</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label small fw-bold text-muted text-uppercase">Category</label>
<select class="form-select form-select-sm bg-light border-0">
<option>All Categories</option>
<option>Sensor Alerts</option>
<option>System State</option>
<option>Access Logs</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label small fw-bold text-muted text-uppercase">Search Description</label>
<input type="text" class="form-control form-control-sm bg-light border-0" placeholder="e.g. Front Door">
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary btn-sm w-100 py-2 fw-bold">APPLY</button>
</div>
</form>
</div>
</div>
<div class="card border-0">
<div class="card-header bg-white py-3 border-bottom d-flex justify-content-between align-items-center">
<h6 class="m-0 fw-bold text-uppercase tracking-wider small">Activity Database</h6>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-secondary border-0"><i class="fas fa-download me-1"></i></button>
<button class="btn btn-sm btn-outline-secondary border-0" hx-get="/api/events/refresh" hx-target="#event-tbody"><i class="fas fa-sync-alt"></i></button>
</div>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th class="severity-strip"></th>
<th class="ps-3">Timestamp</th>
<th>Category</th>
<th>Description</th>
<th>Source</th>
<th class="text-end pe-4">Severity</th>
</tr>
</thead>
<tbody id="event-tbody" class="bg-white">
<tr>
<td class="severity-strip bg-danger"></td>
<td class="ps-3 timestamp">2026-01-09 14:22:05</td>
<td><span class="text-muted small fw-bold">SENSOR</span></td>
<td class="fw-bold text-dark">Front Door <span class="fw-normal text-muted">— Triggered</span></td>
<td class="text-muted small">Reed Switch #01</td>
<td class="text-end pe-4">
<span class="badge bg-danger-subtle text-danger border-0 px-2 py-1 fw-bold">CRITICAL</span>
</td>
</tr>
<tr>
<td class="severity-strip bg-info"></td>
<td class="ps-3 timestamp">2026-01-09 14:21:00</td>
<td><span class="text-muted small fw-bold">SYSTEM</span></td>
<td class="text-dark">System Armed <span class="text-info fw-bold ms-1">AWAY</span></td>
<td class="text-muted small">Web User (Admin)</td>
<td class="text-end pe-4">
<span class="badge bg-info-subtle text-info border-0 px-2 py-1 fw-bold">INFO</span>
</td>
</tr>
<tr>
<td class="severity-strip bg-warning"></td>
<td class="ps-3 timestamp">2026-01-09 13:58:30</td>
<td><span class="text-muted small fw-bold">FAULT</span></td>
<td class="text-dark">Garage Door <span class="text-muted fw-normal">— Comm Timeout</span></td>
<td class="text-muted small">Hub Controller</td>
<td class="text-end pe-4">
<span class="badge bg-warning-subtle text-warning border-0 px-2 py-1 fw-bold">WARNING</span>
</td>
</tr>
<tr>
<td class="severity-strip bg-success"></td>
<td class="ps-3 timestamp">2026-01-09 13:30:15</td>
<td><span class="text-muted small fw-bold">MAINT</span></td>
<td class="text-dark">Self-Diagnostic: <span class="text-success">Passed</span></td>
<td class="text-muted small">System Scheduler</td>
<td class="text-end pe-4">
<span class="badge bg-success-subtle text-success border-0 px-2 py-1 fw-bold">HEALTHY</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer bg-white py-4 text-center border-top-0">
<button class="btn btn-light btn-sm px-4 fw-bold tracking-widest text-muted border">LOAD PREVIOUS 50 EVENTS</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,338 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SecureHome | Dashboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--bg-color: #f4f6f8;
--hover-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);
}
body {
background: var(--bg-color);
font-family: 'Segoe UI', Roboto, Arial, sans-serif;
}
.card,
.btn,
.modal-content {
border-radius: 0
}
.card {
border: 1px solid rgba(0, 0, 0, .05);
box-shadow: 0 1px 3px rgba(0, 0, 0, .1);
transition: .25s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: var(--hover-shadow);
}
.navbar {
background: #1e293b;
box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
}
.sensor-card {
position: relative;
overflow: hidden
}
.status-strip {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
}
.live-dot {
width: 8px;
height: 8px;
background: #22c55e;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(34, 197, 94, .7)
}
70% {
box-shadow: 0 0 0 6px rgba(34, 197, 94, 0)
}
100% {
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0)
}
}
.btn-lg {
font-size: .8rem;
letter-spacing: 1px;
font-weight: 600;
text-transform: uppercase;
padding: 1rem 1.5rem;
}
.table thead th {
font-size: .7rem;
text-transform: uppercase;
color: #64748b;
}
</style>
</head>
<body>
<!-- NAVBAR -->
<nav class="navbar navbar-expand-lg navbar-dark mb-5">
<div class="container">
<span class="navbar-brand fw-bold">
<i class="fas fa-shield-alt me-2 text-info"></i>
SECURE<span class="fw-light">HOME</span>
</span>
<div class="d-flex align-items-center text-light small">
<span class="live-dot me-2"></span>
</div>
</div>
</nav>
<div class="container pb-5">
<!-- SYSTEM OVERVIEW -->
<div class="row mb-4 g-4">
<div class="col-md-4">
<div class="card p-3 h-100">
<div class="card-body d-flex justify-content-between">
<div>
<div class="d-inline-flex align-items-center">
<p class=" m-0 me-2 text-muted small fw-bold">System State</p>
<i class="fas fa-shield-alt text-success opacity-25"></i>
</div>
<h2 id="system-state" class="fw-bold text-success">DISARMED</h2>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card p-3 h-100">
<div class="card-body d-flex justify-content-between">
<div>
<div class="d-inline-flex align-items-center">
<p class="m-0 me-1 text-muted small fw-bold">Active Sensors</p>
<i class="fas fa-wifi text-primary opacity-25"></i>
</div>
<h2 id="sensor-count" class="fw-bold text-primary">4</h2>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card p-3 h-100">
<div class="card-body d-flex justify-content-between">
<div>
<div class="d-inline-flex align-items-center">
<p class="m-0 me-1 text-muted small fw-bold me-2">Uptime</p>
<i class="fas fa-server opacity-25"></i>
</div>
<h2 id="uptime" class="fw-bold">--</h2>
</div>
</div>
</div>
</div>
</div>
<!-- QUICK ACTIONS -->
<div class="card mb-5 border-0">
<div class="card-body p-4">
<h6 class="text-uppercase text-muted fw-bold mb-3">
<i class="fas fa-sliders-h me-2"></i>Quick Actions
</h6>
<div class="d-flex gap-3 flex-wrap">
<button class="btn btn-outline-danger btn-lg flex-grow-1">
<i class="fas fa-lock me-2"></i>ARM SYSTEM
</button>
<button class="btn btn-success btn-lg flex-grow-1 text-white">
<i class="fas fa-lock-open me-2"></i>DISARM SYSTEM
</button>
<button class="btn btn-danger btn-lg flex-grow-1">
<i class="fas fa-bell me-2"></i>PANIC
</button>
<button class="btn btn-dark btn-lg flex-grow-1" data-bs-toggle="modal"
data-bs-target="#scheduleModal">
<i class="far fa-clock me-2"></i>SCHEDULE
</button>
</div>
</div>
</div>
<!-- SENSORS -->
<div class="mb-5">
<h5 class="fw-bold mb-3">Sensors</h5>
<div class="row g-4">
<!-- SENSOR TEMPLATE -->
<div class="col-md-3" id="sensor-front_door">
<div class="card sensor-card h-100 pt-3">
<div class="status-strip bg-success"></div>
<div class="card-body text-center">
<i class="sensor-icon fas fa-door-open fa-2x text-success mb-3"></i>
<h6 class="fw-bold">Front Door</h6>
<span class="sensor-badge badge bg-success-subtle text-success">SECURE</span>
<div class="mt-3 pt-3 border-top">
<small class="text-muted">Updated: <span class="sensor-updated">--:--</span></small>
</div>
</div>
</div>
</div>
<div class="col-md-3" id="sensor-back_door">
<div class="card sensor-card h-100 pt-3">
<div class="status-strip bg-success"></div>
<div class="card-body text-center">
<i class="sensor-icon fas fa-door-open fa-2x text-success mb-3"></i>
<h6 class="fw-bold">Back Door</h6>
<span class="sensor-badge badge bg-success-subtle text-success">SECURE</span>
<div class="mt-3 pt-3 border-top">
<small class="text-muted">Updated: <span class="sensor-updated">--:--</span></small>
</div>
</div>
</div>
</div>
<div class="col-md-3" id="sensor-garage">
<div class="card sensor-card h-100 pt-3">
<div class="status-strip bg-success"></div>
<div class="card-body text-center">
<i class="sensor-icon fas fa-warehouse fa-2x text-success mb-3"></i>
<h6 class="fw-bold">Garage</h6>
<span class="sensor-badge badge bg-success-subtle text-success">SECURE</span>
<div class="mt-3 pt-3 border-top">
<small class="text-muted">Updated: <span class="sensor-updated">--:--</span></small>
</div>
</div>
</div>
</div>
<div class="col-md-3" id="sensor-gate">
<div class="card sensor-card h-100 pt-3">
<div class="status-strip bg-success"></div>
<div class="card-body text-center">
<i class="sensor-icon fas fa-torii-gate fa-2x text-success mb-3"></i>
<h6 class="fw-bold">Main Gate</h6>
<span class="sensor-badge badge bg-success-subtle text-success">SECURE</span>
<div class="mt-3 pt-3 border-top">
<small class="text-muted">Updated: <span class="sensor-updated">--:--</span></small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ACTIVITY LOG -->
<div class="card">
<div class="card-header fw-bold text-uppercase text-muted">Activity Log</div>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Time</th>
<th>Type</th>
<th>Detail</th>
<th>Status</th>
</tr>
</thead>
<tbody id="activity-log"></tbody>
</table>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="scheduleModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Schedule</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">Configuration coming soon.</div>
</div>
</div>
</div>
<!-- JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
<script>
const socket = new SockJS('/ws');
const stomp = Stomp.over(socket);
stomp.debug = null;
stomp.connect({}, () => {
stomp.subscribe('/topic/sensors', msg => updateSensor(JSON.parse(msg.body)));
});
function updateSensor(sensor) {
const card = document.getElementById(`sensor-${sensor.sensorId}`);
if (!card) return;
const open = sensor.state === 'OPEN';
card.querySelector('.status-strip').className =
'status-strip ' + (open ? 'bg-danger' : 'bg-success');
const icon = card.querySelector('.sensor-icon');
icon.classList.toggle('text-danger', open);
icon.classList.toggle('text-success', !open);
const badge = card.querySelector('.sensor-badge');
badge.textContent = open ? 'OPEN' : 'SECURE';
badge.className =
'sensor-badge badge ' +
(open ? 'bg-danger-subtle text-danger' : 'bg-success-subtle text-success');
card.querySelector('.sensor-updated').textContent =
new Date(sensor.ts).toLocaleTimeString();
logActivity(sensor);
}
function logActivity(sensor) {
const row = document.createElement('tr');
row.innerHTML = `
<td>${new Date(sensor.ts).toLocaleTimeString()}</td>
<td>SENSOR</td>
<td>${sensor.sensorId} ${sensor.state}</td>
<td><span class="badge ${sensor.state === 'OPEN' ? 'bg-danger' : 'bg-success'}">
${sensor.state}
</span></td>`;
document.getElementById('activity-log').prepend(row);
}
</script>
</body>
</html>

View File

@ -0,0 +1,251 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SecureHome | System Configuration</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono&display=swap"
rel="stylesheet">
<style>
:root {
--bs-body-bg: #f4f6f8;
--bs-body-font-family: 'Inter', sans-serif;
--accent-dark: #1e293b;
--accent-info: #0dcaf0;
}
/* --- GLOBAL SHARP UI --- */
.card,
.btn,
.form-control,
.form-select,
.badge,
.input-group-text {
border-radius: 0 !important;
border-color: #e2e8f0;
}
.navbar {
background-color: var(--accent-dark) !important;
}
/* --- SECTION STYLING --- */
.settings-container {
max-width: 1000px;
margin: 0 auto;
}
.section-header {
background-color: var(--accent-dark);
color: white;
padding: 0.75rem 1.25rem;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 1.5px;
font-weight: 700;
margin-bottom: 0;
}
.form-label {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 700;
color: #64748b;
}
.table thead th {
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 700;
color: #64748b;
background-color: #f8fafc;
border-bottom: 2px solid #e2e8f0;
}
/* --- INPUT HIGHLIGHTS --- */
.form-control:focus,
.form-select:focus {
box-shadow: none;
border-color: var(--accent-info);
background-color: #fff;
}
.input-group-text {
background-color: #f8fafc;
font-size: 0.8rem;
font-weight: 600;
color: #94a3b8;
}
/* --- TOGGLE STYLING --- */
.form-check-input:checked {
background-color: var(--accent-info);
border-color: var(--accent-info);
}
/* --- CUSTOM ACTION BAR --- */
.action-bar {
position: sticky;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(8px);
border-top: 2px solid var(--accent-dark);
z-index: 1000;
margin-top: 3rem;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark shadow-sm">
<div class="container">
<a class="navbar-brand d-flex align-items-center gap-2" href="#">
<i class="bi bi-shield-lock-fill text-info"></i>
<span class="fw-bold tracking-tight">SECURE<span class="fw-light">HOME</span></span>
</a>
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link" href="#">Dashboard</a></li>
<li class="nav-item"><a class="nav-link" href="#">Cameras</a></li>
<li class="nav-item"><a class="nav-link active" href="#">Settings</a></li>
</ul>
</div>
</div>
</nav>
<div class="container py-5">
<div class="settings-container">
<form id="systemSettingsForm">
<div class="card border-0 shadow-sm mb-5">
<div class="section-header">General Configuration</div>
<div class="card-body p-4">
<div class="row g-4">
<div class="col-md-4">
<label class="form-label">Entry Delay</label>
<div class="input-group">
<input type="number" class="form-control bg-light" value="30">
<span class="input-group-text">SEC</span>
</div>
</div>
<div class="col-md-4">
<label class="form-label">Exit Delay</label>
<div class="input-group">
<input type="number" class="form-control bg-light" value="45">
<span class="input-group-text">SEC</span>
</div>
</div>
<div class="col-md-4">
<label class="form-label">Siren Timeout</label>
<div class="input-group">
<input type="number" class="form-control bg-light" value="180">
<span class="input-group-text">SEC</span>
</div>
</div>
</div>
</div>
</div>
<div class="card border-0 shadow-sm mb-5">
<div class="section-header d-flex justify-content-between align-items-center">
<span>User Management</span>
<button type="button" class="btn btn-sm btn-info text-white fw-bold py-0"
style="font-size: 0.65rem;">+ ADD USER</button>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th class="ps-4">Identity</th>
<th>Access Level</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ps-4">
<div class="d-flex align-items-center gap-3 py-1">
<div class="bg-dark text-white d-flex align-items-center justify-content-center"
style="width:32px; height:32px; font-size: 0.8rem;">AD</div>
<span class="fw-bold">Administrator</span>
</div>
</td>
<td><span class="badge bg-dark-subtle text-dark px-3 py-2 border">FULL ACCESS</span>
</td>
<td class="text-end pe-4">
<button class="btn btn-sm btn-light border-0"><i
class="bi bi-pencil-square"></i></button>
<button class="btn btn-sm btn-light border-0 text-danger"><i
class="bi bi-trash"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card border-0 shadow-sm mb-5">
<div class="section-header d-flex justify-content-between align-items-center">
<span>Automation Schedules</span>
<button type="button" class="btn btn-sm btn-info text-white fw-bold py-0"
style="font-size: 0.65rem;">+ ADD SCHEDULE</button>
</div>
<div class="card-body p-0">
<div class="list-group list-group-flush">
<div class="list-group-item p-4 d-flex justify-content-between align-items-center border-0">
<div class="d-flex gap-4 align-items-center">
<i class="bi bi-moon-stars text-info fs-4"></i>
<div>
<h6 class="mb-0 fw-bold">Weekday Night Arm</h6>
<code class="text-muted"
style="font-size: 0.75rem;">Type: AWAY | Time: 22:00 | Repeat: MON-FRI</code>
</div>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" checked>
</div>
</div>
</div>
</div>
</div>
<div class="card border-0 shadow-sm mb-5">
<div class="section-header">Notification Triggers</div>
<div class="card-body p-4">
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" checked id="chan1">
<label class="small fw-medium ms-2" for="chan1">Push Notifications (Mobile)</label>
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="chan2">
<label class="small fw-medium ms-2" for="chan2">Critical Email Alerts</label>
</div>
</div>
</div>
<div class="action-bar p-4">
<div class="container d-flex justify-content-end gap-3 align-items-center">
<button type="button" class="btn btn-link text-decoration-none text-muted fw-bold small">DISCARD
CHANGES</button>
<button type="submit" class="btn btn-dark px-5 py-2 fw-bold tracking-wider">SAVE ALL
SETTINGS</button>
</div>
</div>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,24 @@
## 🛠️ Tech Stack
### Backend
* **Java 25:** Utilizing the latest LTS features for a modern development experience.
* **Spring Boot:** The core framework for rapid application development.
* **Spring Security:** Ensuring robust authentication and data protection.
* **Spring WebSocket:** Enabling real-time bi-directional communication for sensor updates.
### Hardware Integration
* **Arduino:** Microcontroller for interfacing with physical sensors.
* **jSerialComm:** Java library for platform-independent serial port access to communicate with the Arduino.
### Frontend & Interactivity
* **Thymeleaf:** Server-side Java template engine for seamless HTML rendering.
* **HTMX:** Powering fast, AJAX-like interactivity without the complexity of heavy JavaScript frameworks.
* **JavaScript:** Javascript is used to subscribe to the Websocket endpoint and update components for real-time updates.
### Data & Storage
* **Hibernate / JPA:** Standardized Object-Relational Mapping.
* **MySQL:** Reliable relational database for structured sensor log storage.
### Build & DevOps
* **Build Tool**: Maven