prepare intranet for first Dokploy deployment

This commit is contained in:
2026-08-26 12:59:51 +02:00
parent 5cfe688bdc
commit 29dfb85fef
1321 changed files with 227234 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

@@ -0,0 +1,4 @@
<?php
require_once __DIR__ . '/../../general_files/ini/runtime_env.php';
define('HA_BASE_URL', rtrim(intranet_env('HA_BASE_URL'), '/'));
define('HA_TOKEN', intranet_env('HA_TOKEN'));
@@ -0,0 +1,512 @@
<?php
date_default_timezone_set('Europe/Berlin');
require_once __DIR__ . '/../contentpanel/insertion/news_cleanup.php';
// DB-Verbindung zentral laden (liefert PDO-Objekt zurück)
$pdo = require __DIR__ . '/../../general_files/ini/database.php';
$openWeatherApiKey = intranet_env('OPENWEATHER_API_KEY');
$heute = date('Y-m-d');
// --- Gottesdienst (Bibelwort & Eingangslied) ---
$sql = "SELECT bibelwort, bibelvers, eingang_nummer, eingang_titel
FROM gottesdienst
WHERE bibeldatum = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$heute]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) {
$bibelwort = $row['bibelwort'];
$bibelvers = $row['bibelvers'];
$eingangNummer = $row['eingang_nummer'];
$eingangTitel = $row['eingang_titel'];
} else {
$bibelwort = "Bibelwort nicht eingetragen";
$bibelvers = "Bibelvers nicht gefunden.";
$eingangNummer = "N/A";
$eingangTitel = "Eingangslied nicht gefunden";
}
// --- Ansagen ---
$sqlAnsagen = "SELECT ansage_datum_von, ansage_datum_bis, ansage_uhrzeit, ansage_ort, ansage_titel, ansage_untertitel
FROM ansagen
WHERE ansage_datum_von >= ? OR ansage_datum_bis >= ?
ORDER BY ansage_datum_von ASC";
$stmtAnsagen = $pdo->prepare($sqlAnsagen);
$stmtAnsagen->execute([$heute, $heute]);
$ansagenResult = $stmtAnsagen->fetchAll(PDO::FETCH_ASSOC);
// Verbindung schließen (optional, aber sauber)
$pdo = null;
// --- Aushänge ---
$pngDirectoryUrl = '../contentpanel/insertion/news/png/';
// Abgelaufene Aushänge einschließlich Original-PDF bereinigen
cleanupExpiredNews();
$pngDirectoryFs = realpath(
__DIR__ . '/../contentpanel/insertion/news/png'
);
$pngFiles = ($pngDirectoryFs && is_dir($pngDirectoryFs))
? scandir($pngDirectoryFs)
: [];
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NAK Reinickendorf</title>
<link rel="icon" type="image/svg+xml" href="/intranet_repo/projects/_central/img/nak-logo.svg">
<link rel="shortcut icon" href="/intranet_repo/projects/_central/img/favicon.ico">
<link rel="stylesheet" href="stylo.css?v=<?php echo time(); ?>">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=SUSE:wght@400;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Anton&display=swap" rel="stylesheet">
</head>
<body>
<div class="content-board">
<div class="content-column-right">
<div class="content-column-right-upper-box">
<div class="content-column-right-upper-box-right">
<div class="content-item-time">
<div class="time">
<div id="uhrzeit"></div>
</div>
<div class="date">
<center><?php
$months = ["UNDEFINED", "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
echo date("d. ") . $months[intval(date("m"))];
?></center>
</div>
</div>
<div class="content-item-daily">
<div class="header-sticky-note">
<div>Heutige Ansagen</div>
</div>
<div class="news-container">
<div class="news-scroll">
<?php foreach ($ansagenResult as $ansage) : ?>
<div class="news-box">
<div class="news-date">
<?php
$wochentage = [
'Mon' => 'Mo','Tue' => 'Di','Wed' => 'Mi','Thu' => 'Do',
'Fri' => 'Fr','Sat' => 'Sa','Sun' => 'So'
];
$datum_von_timestamp = strtotime($ansage['ansage_datum_von']);
$heute = date("Y-m-d");
$morgen = date("Y-m-d", strtotime('+1 day'));
$datum_von_format = date("Y-m-d", $datum_von_timestamp);
if ($datum_von_format === $heute) {
echo "Heute";
} elseif ($datum_von_format === $morgen) {
echo "Morgen";
} else {
$datum_von = date("d.m.", $datum_von_timestamp);
$wochentag_englisch = date("D", $datum_von_timestamp);
$wochentag = $wochentage[$wochentag_englisch] ?? $wochentag_englisch;
echo $wochentag . '&nbsp;' . $datum_von;
}
if (!empty($ansage['ansage_datum_bis'])) {
$datum_bis = date("d.m.", strtotime($ansage['ansage_datum_bis']));
echo "<br> bis " . $datum_bis;
}
?>
</div>
<div>
<?php echo htmlspecialchars($ansage['ansage_titel']); ?>
<?php if (!empty($ansage['ansage_untertitel'])) : ?>
<div class="news-subtitle"><?php echo htmlspecialchars($ansage['ansage_untertitel']); ?></div>
<?php endif; ?>
<?php if (!empty($ansage['ansage_uhrzeit']) && !empty($ansage['ansage_ort'])) : ?>
<div class="news-subtitle"><i><?php echo date("H:i", strtotime($ansage['ansage_uhrzeit'])) . ' Uhr'; ?>, <?php echo htmlspecialchars($ansage['ansage_ort']); ?></i></div>
<?php elseif (!empty($ansage['ansage_uhrzeit'])) : ?>
<div class="news-subtitle"><i><?php echo date("H:i", strtotime($ansage['ansage_uhrzeit'])) . ' Uhr'; ?></i></div>
<?php elseif (!empty($ansage['ansage_ort'])) : ?>
<div class="news-subtitle"><i><?php echo htmlspecialchars($ansage['ansage_ort']); ?></i></div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<div class="content-column-right-upper-box-left">
<div class="content-item-middle-top">
<div class="header-sticky-note">
<div>Bibelwort</div>
</div>
<div class="bible"><?php echo $bibelwort; ?></div>
<div class="verse"><?php echo $bibelvers; ?></div>
</div>
<div class="content-column-double-item">
<div class="content-item">
<div class="header-sticky-note">
<div>Eingangslied</div>
</div>
<div class="GB"><?php echo $eingangNummer; ?></div>
<div class="GB-title"><?php echo $eingangTitel; ?></div>
</div>
<div class="content-item">
<div class="header-sticky-note">
<div>Temperatur</div>
</div>
<div class="temperature">
<div class="container">
<div id="wetter"></div>
</div>
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-5 -5 60 60" width="64px" height="64px" baseProfile="basic">
<polygon fill="#66e1ff" points="46,22.11 46,24.42 43,26.15 43,34.24 25,44.63 14,38.28 14,35.97 12,34.81 10,35.97 7,34.24 7,20.38 4,20.96 4,18.65 22,8.26 34,5.95"/>
<polygon fill="#4292a6" points="25,30.774 28,34.815 43,26.155 43,34.238 25,44.63"/>
<polygon fill="#ff5757" points="16,18.649 4,20.959 4,18.649 16,16.34 28,32.506 28,34.815"/>
<polygon fill="#4292a6" points="34,8.257 22,10.566 22,8.257 34,5.947 46,22.113 46,24.423"/>
<polygon fill="#e64e4e" points="4,18.649 22,8.257 34,5.947 16,16.34"/>
<polygon fill="#cc4545" points="28,32.506 46,22.113 34,5.947 16,16.34"/>
<polygon fill="#b33d3d" points="28,32.506 46,22.113 46,24.423 28,34.815"/>
<polygon fill="#4292a6" points="17,34.238 22,37.124 22,31.351 17,28.464"/>
<polygon fill="#4292a6" points="10,35.97 12,34.815 12,27.887 10,26.732"/>
<polygon fill="#4ad8ff" points="19,33.083 22,34.815 22,31.351 19,29.619"/>
<polygon fill="#52b1cc" points="17,34.238 22,37.124 22,34.815 19,33.083"/>
<polygon fill="#ff5757" points="12,34.815 14,35.97 14,29.041 12,27.887"/>
</svg>
<div class="text-indoor" id='temperature_txt'></div>
</div>
</div>
</div>
</div>
</div> <!-- /left upper box -->
</div> <!-- /right upper -->
<div class="content-item-bottom-box">
<div class="header-sticky-note">
<div>Aushänge</div>
</div>
<div class="aushang-flex">
<?php
$hasPng = false;
foreach ($pngFiles as $file) {
if (pathinfo($file, PATHINFO_EXTENSION) === 'png') {
$hasPng = true;
echo '<img src="' . $pngDirectoryUrl . $file . '" alt="Aushang" height="95%">';
}
}
if (!$hasPng) {
echo '<p style="text-align: left;">Keine Aushänge vorgeplant.</p>';
}
?>
</div>
</div>
</div> <!-- /right column -->
<div class="content-column-left">
<div class="content-item-video">
<!-- leerer Container, kein Video/HLS -->
<div class="video-container" aria-label="Leer"></div>
</div>
<div class="content-item-month">
<div class="header-sticky-note">
<div>Monatsplan</div>
</div>
<div class="content-item-month-cell">
<img src="month/terminuebersicht-1.png?v=<?php echo time(); ?>" alt="image">
<img src="month/terminuebersicht-2.png?v=<?php echo time(); ?>" alt="image">
</div>
</div>
</div> <!-- /left column -->
</div> <!-- /content-board -->
<script>
function updateClock() {
var now = new Date();
var uhrzeitElement = document.getElementById("uhrzeit");
var stunden = now.getHours();
var minuten = now.getMinutes();
uhrzeitElement.innerHTML = + padZero(stunden) + ':' + padZero(minuten);
}
function padZero(number) { return number < 10 ? '0' + number : number; }
updateClock();
setInterval(updateClock, 1000);
// OpenWeather (Außentemperatur+Icon)
const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=52.571291&lon=13.310422&appid=<?= rawurlencode($openWeatherApiKey) ?>&units=metric&language=german`;
// Wetter-Icons
const cloudy = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<defs>
<filter id="blur" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="2.5"/>
<feOffset dx="0" dy="2" result="off"/>
<feComponentTransfer><feFuncA type="linear" slope="0.15"/></feComponentTransfer>
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<g filter="url(#blur)">
<path d="M22 46h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 20 29a9 9 0 0 0 2 17z" fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<path d="M18 46h22a9 9 0 0 0 0-18c-.7 0-1.4.1-2 .2A11 11 0 0 0 17 31a8 8 0 0 0 1 15z" fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round" opacity="0.95"/>
</g>
</svg>`;
const sun = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round">
<circle cx="32" cy="32" r="10" fill="#FFD34D" stroke="#fff"/>
<path d="M32 6v8M32 50v8M6 32h8M50 32h8M13.5 13.5l5.7 5.7M44.8 44.8l5.7 5.7M50.5 13.5l-5.7 5.7M19.2 44.8l-5.7 5.7"/>
</g>
</svg>`;
const moon = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<path d="M41 8a22 22 0 1 0 15 35A18 18 0 0 1 41 8z" fill="#DDE7FF" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
</svg>`;
const cloudy_day = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g>
<g transform="translate(4,0)">
<circle cx="22" cy="22" r="9" fill="#FFD34D" stroke="#fff" stroke-width="2"/>
<g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round">
<path d="M22 6v6M22 32v6M6 22h6M32 22h6M10.5 10.5l4.2 4.2M29.3 29.3l4.2 4.2M33.5 10.5l-4.2 4.2M14.7 29.3l-4.2 4.2"/>
</g>
</g>
<g transform="translate(8,10)">
<path d="M18 36h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 16 19a9 9 0 0 0 2 17z"
fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<path d="M22 36h22a9 9 0 0 0 0-18c-.7 0-1.4.1-2 .2A11 11 0 0 0 21 21a8 8 0 0 0 1 15z"
fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round" opacity="0.95"/>
</g>
</g>
</svg>`;
const cloudy_night = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g>
<path d="M38 10a16 16 0 1 0 11 26A13 13 0 0 1 38 10z" fill="#DDE7FF" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<g transform="translate(10,18)">
<path d="M14 28h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 12 11a9 9 0 0 0 2 17z"
fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<path d="M18 28h22a9 9 0 0 0 0-18c-.7 0-1.4.1-2 .2A11 11 0 0 0 17 13a8 8 0 0 0 1 15z"
fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round" opacity="0.95"/>
</g>
</g>
</svg>`;
const rain_shower = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g>
<path d="M20 36h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 18 19a9 9 0 0 0 2 17z"
fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<g stroke="#fff" stroke-width="2" stroke-linecap="round">
<path d="M24 42l-3 6" />
<path d="M34 42l-3 6" />
<path d="M44 42l-3 6" />
</g>
</g>
</svg>`;
const rain = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g>
<path d="M20 34h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 18 17a9 9 0 0 0 2 17z"
fill="#57A0EE" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<g stroke="#fff" stroke-width="2" stroke-linecap="round">
<path d="M22 40l-3 8" />
<path d="M32 40l-3 8" />
<path d="M42 40l-3 8" />
<path d="M52 40l-3 8" />
</g>
</g>
</svg>`;
const thunder = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g>
<path d="M18 34h28a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 16 17a9 9 0 0 0 2 17z"
fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<path d="M32 36l-6 12h7l-3 10 12-16h-7l3-6z"
fill="#FFD34D" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
</g>
</svg>`;
const snowy = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g>
<path d="M20 34h26a10 10 0 0 0 0-20c-.8 0-1.6.1-2.4.3A13 13 0 0 0 18 17a9 9 0 0 0 2 17z"
fill="#91C0F8" stroke="#fff" stroke-width="2" stroke-linejoin="round"/>
<g fill="#fff">
<circle cx="24" cy="44" r="2"/>
<circle cx="34" cy="44" r="2"/>
<circle cx="44" cy="44" r="2"/>
<circle cx="29" cy="52" r="2"/>
<circle cx="39" cy="52" r="2"/>
</g>
</g>
</svg>`;
const mist = `<svg xmlns="http://www.w3.org/2000/svg" width="49" height="49" viewBox="6 0 50 50" aria-hidden="true">
<g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" opacity="0.95">
<path d="M14 24h36"/>
<path d="M10 32h44"/>
<path d="M14 40h36"/>
<path d="M18 48h28"/>
</g>
</svg>`;
async function getWeather() {
try {
var response = await fetch(apiUrl);
var data = await response.json();
if (data.cod === 200) {
var weatherIcon = data.weather[0].icon;
var temperature = data.main.temp;
var weatherElement = document.getElementById('wetter');
if (["04d","04n","03d","03n"].includes(weatherIcon)) {
weatherElement.innerHTML = '<div>' + cloudy + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (weatherIcon === "02d") {
weatherElement.innerHTML = '<div>' + cloudy_day + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (weatherIcon === "02n") {
weatherElement.innerHTML = '<div>' + cloudy_night + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (weatherIcon === "01d") {
weatherElement.innerHTML = '<div>' + sun + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (weatherIcon === "01n") {
weatherElement.innerHTML = '<div>' + moon + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (["09d","09n","10n"].includes(weatherIcon)) {
weatherElement.innerHTML = '<div>' + rain_shower + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (weatherIcon === "10d") {
weatherElement.innerHTML = '<div>' + rain + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (["11d","11n"].includes(weatherIcon)) {
weatherElement.innerHTML = '<div>' + thunder + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (["13d","13n"].includes(weatherIcon)) {
weatherElement.innerHTML = '<div>' + snowy + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
} else if (["50d","50n"].includes(weatherIcon)) {
weatherElement.innerHTML = '<div>' + mist + `</div><div class="text-outdoor"><center>${Math.round(temperature)}°C</center></div>`;
}
} else {
console.error('Fehler beim Abrufen des Wetters:', data.message);
}
} catch (error) {
console.error('Fehler beim Abrufen des Wetters:', error.message);
}
}
getWeather();
setInterval(getWeather, 30000);
// INNENTEMPERATUR: jetzt über Home Assistant (Server-Proxy)
function updateTemperature() {
const entityId = 'sensor.temp_temperatur';
fetch('ha_temperature.php?entity_id=' + encodeURIComponent(entityId))
.then(r => r.json())
.then(data => {
if (data.error) {
console.error('HA Fehler:', data);
return;
}
document.getElementById('temperature_txt').innerText = `${data.value}${data.unit || '°C'}`;
})
.catch(err => console.error('Fetch Fehler:', err));
}
updateTemperature();
setInterval(updateTemperature, 10000);
function updateContent() {
fetch('update_content.php')
.then(response => response.json())
.then(data => {
if (data.error) {
console.error('Fehler:', data.error);
return;
}
document.querySelector('.bible').innerText = data.bibelwort;
document.querySelector('.verse').innerText = data.bibelvers;
document.querySelector('.GB').innerText = data.eingang_nummer;
document.querySelector('.GB-title').innerText = data.eingang_titel;
setupScrolling();
})
.catch(error => {
console.error('Fehler beim Abrufen der Daten:', error);
});
}
updateContent();
setInterval(updateContent, 30000);
function setupScrolling() {
const SPEED_V = 7; // News vertikal
const SPEED_H = 10; // Aushänge horizontal
const containerFlex = document.querySelector('.aushang-flex');
if (containerFlex) {
const images = containerFlex.querySelectorAll('img');
const measureAndApplyH = () => {
let totalWidth = 0;
images.forEach(img => {
const styles = window.getComputedStyle(img);
const mr = parseFloat(styles.marginRight) || 0;
totalWidth += (img.offsetWidth || 0) + mr;
});
const containerWidth = containerFlex.offsetWidth || 0;
const scrollDistanceH = Math.max(0, totalWidth - containerWidth);
containerFlex.style.setProperty('--scroll-distance-h', `-${scrollDistanceH}px`);
const durationH = Math.max(1, scrollDistanceH / SPEED_H);
if (scrollDistanceH === 0) {
containerFlex.style.animation = 'none';
} else {
containerFlex.style.animationName = 'scroll-h';
containerFlex.style.animationDuration = `${durationH}s`;
containerFlex.style.animationTimingFunction = 'linear';
containerFlex.style.animationIterationCount = 'infinite';
}
};
let pending = 0;
images.forEach(img => {
if (!img.complete || img.naturalWidth === 0) {
pending++;
img.addEventListener('load', () => {
pending--;
if (pending === 0) measureAndApplyH();
}, { once: true });
}
});
if (pending === 0) measureAndApplyH();
}
const containerNews = document.querySelector('.news-container');
const scrollBox = document.querySelector('.news-scroll');
if (containerNews && scrollBox) {
const totalHeight = scrollBox.scrollHeight || 0;
const visibleHeight = containerNews.offsetHeight || 0;
const scrollDistanceV = Math.max(0, totalHeight - visibleHeight);
scrollBox.style.setProperty('--scroll-distance-v', `-${scrollDistanceV}px`);
const durationV = Math.max(1, scrollDistanceV / SPEED_V);
if (scrollDistanceV === 0) {
scrollBox.style.animation = 'none';
} else {
scrollBox.style.animationName = 'scroll-v';
scrollBox.style.animationDuration = `${durationV}s`;
scrollBox.style.animationTimingFunction = 'linear';
scrollBox.style.animationIterationCount = 'infinite';
}
}
}
window.addEventListener('load', () => {
const news = document.querySelector('.news-scroll');
const aushang = document.querySelector('.aushang-flex');
if (news) news.style.animation = 'none';
if (aushang) aushang.style.animation = 'none';
setTimeout(setupScrolling, 50);
});
window.addEventListener('resize', () => {
const news = document.querySelector('.news-scroll');
const aushang = document.querySelector('.aushang-flex');
if (news) news.style.animation = 'none';
if (aushang) aushang.style.animation = 'none';
setTimeout(setupScrolling, 100);
});
</script>
</body>
</html>
@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json; charset=utf-8');
// Token/URL aus externer Config laden
require_once __DIR__ . '/config.local.php';
// Entity-ID (Standard: dein Sensor aus der Liste)
$entityId = isset($_GET['entity_id']) ? $_GET['entity_id'] : 'sensor.temp_temperatur';
$url = rtrim(HA_BASE_URL, '/') . '/api/states/' . urlencode($entityId);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . HA_TOKEN,
'Content-Type: application/json'
],
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 5,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($response === false || $httpCode < 200 || $httpCode >= 300) {
http_response_code(502);
echo json_encode([
'error' => 'Home Assistant request failed',
'details' => $error ?: ('HTTP ' . $httpCode)
]);
exit;
}
$data = json_decode($response, true);
if (!is_array($data) || !isset($data['state'])) {
http_response_code(500);
echo json_encode(['error' => 'Unexpected HA response']);
exit;
}
$val = $data['state'];
$unit = $data['attributes']['unit_of_measurement'] ?? '°C';
$CALIBRATION_OFFSET = +0;
if (is_numeric($val)) {
$number = round((float)$val + $CALIBRATION_OFFSET, 1);
} else {
$number = $val;
}
echo json_encode([
'entity_id' => $entityId,
'value' => $number,
'unit' => $unit,
'friendly_name' => $data['attributes']['friendly_name'] ?? null,
'last_changed' => $data['last_changed'] ?? null,
]);
Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

@@ -0,0 +1,340 @@
/* Gesamtes Seiten-Layout */
body {
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'SUSE', sans-serif;
font-size: 20px;
background-image: url('cloudy.jpg');
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
overflow: hidden;
}
/* Content Board Layout */
.content-board {
background-color: #a8a6a7a7;
width: 99%;
height: 97%;
box-sizing: border-box;
display: flex;
justify-content: space-around;
align-items: center;
border-radius: 0;
padding: 0 6px 7px 3px;
}
.content-column-left {
width: 40%;
height: 100%;
box-sizing: border-box;
}
.content-column-right {
width: 58%;
height: 100%;
box-sizing: border-box;
}
.temperature {
display: flex;
justify-content: space-around;
align-items: center;
padding: 5px 0 0 0;
height: 70%;
}
.verse {
text-align: justify;
color: rgb(67, 67, 67);
padding: 5px 15px 5px 15px;
}
.date {
color: rgb(67, 67, 67);
font-weight: bold;
}
.content-column-right-upper-box-left {
width: 60%;
height: 100%;
box-sizing: border-box;
}
.content-column-right-upper-box-right {
width: 39%;
height: 100%;
box-sizing: border-box;
}
.content-column-right-upper-box {
width: 100%;
height: 51%;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
}
.content-item-time {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #ffffffab;
width: 100%;
height: 30%;
margin: 15px 0 15px 0;
box-sizing: border-box;
}
.content-item-news {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #ffffffab;
width: 100%;
height: 63%;
margin: 15px 0 15px 0;
box-sizing: border-box;
}
.content-item-video {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #cbc5bc;
width: 100%;
height: 40%;
margin: 15px 0 15px 0;
box-sizing: border-box;
}
.content-item-middle-top {
position: relative;
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #cbc5bc;
width: 100%;
height: 60%;
margin: 15px 0 15px 0;
box-sizing: border-box;
overflow: hidden;
}
/* Pseudoelement für den animierten border-bottom Effekt */
.content-item-middle-top::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 15px;
background: linear-gradient(to bottom, transparent, #cbc5bc);
}
/* Monatsplan: jetzt mit weißem Hintergrund */
.content-item-month {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #cbc5bc7d;
width: 100%;
height: 56%;
margin: 15px 0 15px 0;
box-sizing: border-box;
overflow: hidden;
}
.content-item-month-cell {
display: flex;
background-color: #ffffffe5;
justify-content: center; /* Zentriere die Bilder horizontal */
height: 94%;
align-items: center; /* Zentriere die Bilder vertikal */
}
.content-item-month-cell img {
max-width: 100%; /* Verhindert, dass das Bild größer als der Container wird */
max-height: 100%; /* Verhindert Verzerrungen, indem es die maximale Höhe auf die Containerhöhe beschränkt */
object-fit: contain; /* Stellt sicher, dass das Bild im Container bleibt, ohne gestreckt zu werden */
}
.content-item-daily {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #edebe8;
width: 100%;
height: 63%;
margin: 15px 0 15px 0;
box-sizing: border-box;
overflow: hidden;
}
.header-sticky-note {
font-size: 25px;
display: flex;
flex-direction: column;
align-items: center;
height: 20px;
width: 100%;
padding: 5px 0 15px 0;
border-bottom: 2px solid #38312430;
background-color: #cbc5bc;
border-radius: 0;
}
.time {
font-size: 80px;
text-align: center;
padding: 8px 0 0 0;
}
.bible {
text-align: center;
font-size: 60px;
padding: 10px 0 10px 0;
border-bottom: 2px solid #38312456;
background-color: #edebe8;
}
.GB {
text-align: center;
font-size: 60px;
padding: 5px 0 0 0;
}
.GB-title {
text-align: center;
color: rgb(67, 67, 67);
}
.content-column-double-item {
width: 100%;
height: 33%;
margin: 15px 0 15px 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
}
/* Elemente innerhalb des Content Boards */
.content-item {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #edebe8;
height: 100%;
width: 49%;
}
.content-item-bottom-box {
border-radius: 0;
box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.16);
background-color: #edebe8;
margin: 6px 0 0 0;
height: 47.5%;
width: 100%;
overflow: hidden;
}
.video-container {
border-radius: 0;
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
border: 5px solid #cbc5bc;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.video-container video {
width: 100%;
height: 100%;
transform: scale(1.2);
object-fit: cover;
border-radius: 0;
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 40%;
background-color: #38312431;
border-radius: 0;
border: 2px solid #38312440;
}
.text-outdoor {
font-weight: bold;
padding: 9px 0 0 0;
margin: 0 0 15px 0;
}
.text-indoor {
font-weight: bold;
margin: 0 0 15px 0;
}
.news-box {
display: flex;
justify-content: flex-start;
padding: 5px 15px 5px 15px;
border-bottom: 2px solid #38312456;
background-color: #edebe8;
word-wrap: break-word;
}
.news-date {
font-weight: bold;
margin: 0 10px 0 0;
}
.news-subtitle {
color: rgba(67, 67, 67, 0.657);
}
.news-container {
height: 290px;
overflow: hidden;
position: relative;
}
.news-scroll {
display: flex;
flex-direction: column;
animation-name: scroll-v;
animation-timing-function: linear;
animation-iteration-count: infinite;
--scroll-distance-v: 0px;
}
@keyframes scroll-v {
0% { transform: translateY(0); }
100% { transform: translateY(var(--scroll-distance-v)); }
}
.aushang-flex img {
border-radius: 0;
margin: 0 10px 0 0;
border: 2px solid #38312440;
box-shadow: 4px 4px 2px rgba(0, 0, 0, 0.08);
}
.aushang-flex {
padding: 0 5px 0 5px;
display: flex;
justify-content: space-evenly;
align-items: center;
height: 90.5%;
border-radius: 0;
animation-name: scroll-h;
animation-timing-function: linear;
animation-iteration-count: infinite;
--scroll-distance-h: 0px;
}
@keyframes scroll-h {
0% { transform: translateX(0); }
100% { transform: translateX(var(--scroll-distance-h)); }
}
@@ -0,0 +1,54 @@
<?php
header('Content-Type: application/json; charset=utf-8');
date_default_timezone_set('Europe/Berlin');
// DB-Verbindung zentral laden
$pdo = require __DIR__ . '/../../general_files/ini/database.php';
$heute = date('Y-m-d');
try {
// --- Bibelwort, Bibelvers und Eingangslied ---
$sql = "SELECT bibelwort, bibelvers, eingang_nummer, eingang_titel
FROM gottesdienst
WHERE bibeldatum = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$heute]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) {
$bibelwort = $row['bibelwort'];
$bibelvers = $row['bibelvers'];
$eingangNummer = $row['eingang_nummer'];
$eingangTitel = $row['eingang_titel'];
} else {
$bibelwort = "Bibelwort nicht eingetragen";
$bibelvers = "Bibelvers nicht gefunden.";
$eingangNummer = "N/A";
$eingangTitel = "Eingangslied nicht gefunden";
}
// --- Ansagen ---
$sqlAnsagen = "SELECT ansage_datum_von, ansage_datum_bis, ansage_uhrzeit, ansage_ort, ansage_titel, ansage_untertitel
FROM ansagen
WHERE ansage_datum_von >= ? OR ansage_datum_bis >= ?
ORDER BY ansage_datum_von ASC";
$stmtAnsagen = $pdo->prepare($sqlAnsagen);
$stmtAnsagen->execute([$heute, $heute]);
$ansagenResult = $stmtAnsagen->fetchAll(PDO::FETCH_ASSOC);
// Rückgabe als JSON
echo json_encode([
'bibelwort' => $bibelwort,
'bibelvers' => $bibelvers,
'eingang_nummer' => $eingangNummer,
'eingang_titel' => $eingangTitel,
'ansagen' => $ansagenResult
], JSON_UNESCAPED_UNICODE);
} catch (Throwable $e) {
// Optional: error_log($e->getMessage());
echo json_encode(['error' => 'Fehler bei der Verbindung zur Datenbank'], JSON_UNESCAPED_UNICODE);
} finally {
$pdo = null;
}