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:
parent
1e6449a89d
commit
4955e4a820
|
|
@ -17,7 +17,7 @@ Sensor sensors[] = {
|
|||
const uint8_t SENSOR_COUNT = sizeof(sensors) / sizeof(sensors[0]);
|
||||
|
||||
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;
|
||||
|
||||
unsigned long lastPollTime = 0;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
15
future.md
15
future.md
|
|
@ -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
12
pom.xml
|
|
@ -84,6 +84,18 @@
|
|||
<scope>test</scope>
|
||||
</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>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security-oauth2-client</artifactId>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
|
|||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Component
|
||||
public class ArduinoListener {
|
||||
|
|
@ -23,6 +25,8 @@ public class ArduinoListener {
|
|||
private SerialPort arduinoPort;
|
||||
private final StringBuilder buffer = new StringBuilder();
|
||||
private ObjectMapper objectMapper;
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
|
||||
@Autowired
|
||||
private SensorService sensorService;
|
||||
|
|
@ -77,7 +81,8 @@ public class ArduinoListener {
|
|||
|
||||
buffer.append(new String(data, StandardCharsets.UTF_8));
|
||||
|
||||
processBuffer();
|
||||
executor.submit(() -> processBuffer());
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,12 +12,12 @@ public class SecurityConfig {
|
|||
public SecurityFilterChain securityFilterChain(HttpSecurity http){
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/login")
|
||||
.requestMatchers("/")
|
||||
.permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.oauth2Login(oauth2 -> oauth2
|
||||
.defaultSuccessUrl("/", true)
|
||||
.defaultSuccessUrl("/dashboard", true)
|
||||
)
|
||||
.logout(logout -> logout
|
||||
.logoutSuccessUrl("/")
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
24
stack.md
24
stack.md
|
|
@ -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
|
||||
Loading…
Reference in New Issue