309 lines
9.3 KiB
HTML
309 lines
9.3 KiB
HTML
<!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>
|
|
<div class="container">
|
|
<h1>🏠 Zuhause</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>
|
|
// API configuration from backend
|
|
window.API_BASE = '{{ api_base }}';
|
|
</script>
|
|
|
|
<!-- Load API client AFTER API_BASE is set -->
|
|
<script src="/static/types.js"></script>
|
|
<script src="/static/api-client.js"></script>
|
|
|
|
<script>
|
|
// 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 and devices using API client
|
|
// NEW: Use device layout endpoint for each device
|
|
// Fallback: load all rooms as before
|
|
const layoutData = await window.apiClient.getLayout();
|
|
const devicesData = await window.apiClient.getDevices();
|
|
// Example: For each device, you could also fetch layout info via
|
|
// await window.apiClient.fetch(window.apiClient.api(`/devices/${device_id}/layout`));
|
|
|
|
// 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(roomDevices, deviceMap) {
|
|
// Extract device IDs from room devices (they are objects now)
|
|
const deviceIds = roomDevices.map(d => d.device_id || d);
|
|
|
|
// 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>
|