new ui 1
This commit is contained in:
306
apps/ui/templates/rooms.html
Normal file
306
apps/ui/templates/rooms.html
Normal file
@@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Räume - Home Automation</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: white;
|
||||
font-size: 28px;
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rooms-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.rooms-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.rooms-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.room-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
min-height: 110px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.room-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.room-card:active {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.room-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.room-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.room-device-count {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.room-stats {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: rgba(255, 59, 48, 0.9);
|
||||
color: white;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #667eea;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/" class="back-button">← Dashboard</a>
|
||||
|
||||
<div class="container">
|
||||
<h1>🏠 Räume</h1>
|
||||
|
||||
<div id="error-container"></div>
|
||||
<div id="loading" class="loading">Lade Räume...</div>
|
||||
<div id="rooms-grid" class="rooms-grid" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = window.location.origin;
|
||||
|
||||
// Room icon mapping
|
||||
const roomIcons = {
|
||||
'wohnzimmer': '🛋️',
|
||||
'küche': '🍳',
|
||||
'kueche': '🍳',
|
||||
'schlafzimmer': '🛏️',
|
||||
'bad': '🚿',
|
||||
'badezimmer': '🚿',
|
||||
'bad oben': '🚿',
|
||||
'bad unten': '🚿',
|
||||
'flur': '🚪',
|
||||
'büro': '💼',
|
||||
'buero': '💼',
|
||||
'arbeitszimmer': '💼',
|
||||
'studierzimmer': '📚',
|
||||
'esszimmer': '🍽️',
|
||||
'garten': '🌳',
|
||||
'terrasse': '🌿',
|
||||
'garage': '🚗',
|
||||
'keller': '🔧',
|
||||
'dachboden': '📦',
|
||||
'kinderzimmer': '🧸',
|
||||
'patty': '👤',
|
||||
'wolfgang': '👤',
|
||||
'default': '🏡'
|
||||
};
|
||||
|
||||
function getRoomIcon(roomName) {
|
||||
const normalized = roomName.toLowerCase().trim();
|
||||
return roomIcons[normalized] || roomIcons['default'];
|
||||
}
|
||||
|
||||
async function loadRooms() {
|
||||
const loading = document.getElementById('loading');
|
||||
const grid = document.getElementById('rooms-grid');
|
||||
const errorContainer = document.getElementById('error-container');
|
||||
|
||||
try {
|
||||
// Load layout
|
||||
const layoutResponse = await fetch(`${API_BASE}/layout`);
|
||||
if (!layoutResponse.ok) {
|
||||
throw new Error(`Layout API error: ${layoutResponse.status}`);
|
||||
}
|
||||
const layoutData = await layoutResponse.json();
|
||||
|
||||
// Load devices for feature checks
|
||||
const devicesResponse = await fetch(`${API_BASE}/devices`);
|
||||
if (!devicesResponse.ok) {
|
||||
throw new Error(`Devices API error: ${devicesResponse.status}`);
|
||||
}
|
||||
const devicesData = await devicesResponse.json();
|
||||
|
||||
// Create device lookup
|
||||
const deviceMap = {};
|
||||
devicesData.forEach(device => {
|
||||
deviceMap[device.device_id] = device;
|
||||
});
|
||||
|
||||
// Render rooms
|
||||
loading.style.display = 'none';
|
||||
grid.style.display = 'grid';
|
||||
|
||||
layoutData.rooms.forEach(room => {
|
||||
const card = createRoomCard(room, deviceMap);
|
||||
grid.appendChild(card);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading rooms:', error);
|
||||
loading.style.display = 'none';
|
||||
errorContainer.innerHTML = `
|
||||
<div class="error">
|
||||
⚠️ Fehler beim Laden der Räume: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function createRoomCard(room, deviceMap) {
|
||||
const card = document.createElement('a');
|
||||
card.className = 'room-card';
|
||||
card.href = `/room/${encodeURIComponent(room.name)}`;
|
||||
|
||||
const icon = document.createElement('div');
|
||||
icon.className = 'room-icon';
|
||||
icon.textContent = getRoomIcon(room.name);
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.className = 'room-name';
|
||||
name.textContent = room.name;
|
||||
|
||||
const deviceCount = document.createElement('div');
|
||||
deviceCount.className = 'room-device-count';
|
||||
deviceCount.textContent = `${room.devices.length} Gerät${room.devices.length !== 1 ? 'e' : ''}`;
|
||||
|
||||
// Optional: Calculate stats (lights on, windows open, etc.)
|
||||
const stats = calculateRoomStats(room.devices, deviceMap);
|
||||
if (stats) {
|
||||
const statsDiv = document.createElement('div');
|
||||
statsDiv.className = 'room-stats';
|
||||
statsDiv.textContent = stats;
|
||||
card.appendChild(icon);
|
||||
card.appendChild(name);
|
||||
card.appendChild(deviceCount);
|
||||
card.appendChild(statsDiv);
|
||||
} else {
|
||||
card.appendChild(icon);
|
||||
card.appendChild(name);
|
||||
card.appendChild(deviceCount);
|
||||
}
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function calculateRoomStats(deviceIds, deviceMap) {
|
||||
// Count device types
|
||||
let lights = 0;
|
||||
let thermostats = 0;
|
||||
let contacts = 0;
|
||||
let sensors = 0;
|
||||
|
||||
deviceIds.forEach(deviceId => {
|
||||
const device = deviceMap[deviceId];
|
||||
if (!device) return;
|
||||
|
||||
switch(device.type) {
|
||||
case 'light':
|
||||
lights++;
|
||||
break;
|
||||
case 'thermostat':
|
||||
thermostats++;
|
||||
break;
|
||||
case 'contact':
|
||||
contacts++;
|
||||
break;
|
||||
case 'temp_humidity_sensor':
|
||||
sensors++;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Build compact stats string
|
||||
const parts = [];
|
||||
if (lights > 0) parts.push(`💡${lights}`);
|
||||
if (thermostats > 0) parts.push(`🌡️${thermostats}`);
|
||||
if (contacts > 0) parts.push(`🚪${contacts}`);
|
||||
|
||||
return parts.length > 0 ? parts.join(' ') : null;
|
||||
}
|
||||
|
||||
// Load rooms on page load
|
||||
document.addEventListener('DOMContentLoaded', loadRooms);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user