Files
intranet/web/intranet_repo/projects/dashboard/util/dashboard_helpers.php
T

258 lines
8.2 KiB
PHP

<?php
require_once __DIR__ . '/../../contentpanel/insertion/util/contentpanel_helpers.php';
require_once __DIR__ . '/../../../general_files/ini/runtime_env.php';
const DASHBOARD_TIMEZONE = 'Europe/Berlin';
const DASHBOARD_HA_ENTITY_ID = 'sensor.temp_temperatur';
const DASHBOARD_OPENWEATHER_LATITUDE = '52.571291';
const DASHBOARD_OPENWEATHER_LONGITUDE = '13.310422';
const DASHBOARD_NEWS_URL = '/intranet_repo/projects/contentpanel/insertion/news/png/';
function dashboard_now(): DateTimeImmutable
{
return new DateTimeImmutable('now', new DateTimeZone(DASHBOARD_TIMEZONE));
}
function dashboard_html($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function dashboard_fetch_content(PDO $pdo, DateTimeImmutable $now): array
{
$today = $now->format('Y-m-d');
$serviceStatement = $pdo->prepare(
'SELECT bibelwort, bibelvers, eingang_nummer, eingang_titel
FROM gottesdienst
WHERE bibeldatum = ?'
);
$serviceStatement->execute([$today]);
$service = $serviceStatement->fetch(PDO::FETCH_ASSOC) ?: [];
$announcementStatement = $pdo->prepare(
'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'
);
$announcementStatement->execute([$today, $today]);
$announcements = $announcementStatement->fetchAll(PDO::FETCH_ASSOC);
foreach ($announcements as &$announcement) {
$announcement = dashboard_prepare_announcement($announcement, $now);
}
unset($announcement);
contentpanel_cleanup_expired_news();
return [
'date_label' => dashboard_date_label($now),
'service' => [
'bibelwort' => (string) ($service['bibelwort'] ?? 'Bibelwort nicht eingetragen'),
'bibelvers' => (string) ($service['bibelvers'] ?? 'Bibelvers nicht gefunden.'),
'eingang_nummer' => (string) ($service['eingang_nummer'] ?? 'N/A'),
'eingang_titel' => (string) ($service['eingang_titel'] ?? 'Eingangslied nicht gefunden'),
],
'announcements' => $announcements,
'posters' => dashboard_list_posters(),
'month_images' => dashboard_month_images(),
];
}
function dashboard_date_label(DateTimeImmutable $date): string
{
static $months = [
1 => 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember',
];
return sprintf('%02d. %s', (int) $date->format('d'), $months[(int) $date->format('n')]);
}
function dashboard_prepare_announcement(array $announcement, DateTimeImmutable $now): array
{
$startRaw = substr((string) ($announcement['ansage_datum_von'] ?? ''), 0, 10);
$endRaw = substr((string) ($announcement['ansage_datum_bis'] ?? ''), 0, 10);
$timeRaw = substr((string) ($announcement['ansage_uhrzeit'] ?? ''), 0, 5);
$startDate = DateTimeImmutable::createFromFormat('!Y-m-d', $startRaw, $now->getTimezone());
$tomorrow = $now->setTime(0, 0)->modify('+1 day');
if ($startDate && $startDate->format('Y-m-d') === $now->format('Y-m-d')) {
$dateLabel = 'Heute';
} elseif ($startDate && $startDate->format('Y-m-d') === $tomorrow->format('Y-m-d')) {
$dateLabel = 'Morgen';
} elseif ($startDate) {
static $weekdays = [1 => 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
$dateLabel = $weekdays[(int) $startDate->format('N')] . ' ' . $startDate->format('d.m.');
} else {
$dateLabel = $startRaw;
}
$endLabel = '';
if ($endRaw !== '') {
$endDate = DateTimeImmutable::createFromFormat('!Y-m-d', $endRaw, $now->getTimezone());
$endLabel = $endDate ? 'bis ' . $endDate->format('d.m.') : 'bis ' . $endRaw;
}
$meta = [];
if ($timeRaw !== '') {
$meta[] = $timeRaw . ' Uhr';
}
$place = trim((string) ($announcement['ansage_ort'] ?? ''));
if ($place !== '') {
$meta[] = $place;
}
return [
'date_label' => $dateLabel,
'end_label' => $endLabel,
'title' => (string) ($announcement['ansage_titel'] ?? ''),
'subtitle' => (string) ($announcement['ansage_untertitel'] ?? ''),
'meta' => implode(', ', $meta),
];
}
function dashboard_list_posters(): array
{
$files = glob(CONTENTPANEL_NEWS_PNG_DIRECTORY . '/*.png') ?: [];
natsort($files);
$posters = [];
foreach ($files as $file) {
if (!is_file($file)) {
continue;
}
$basename = basename($file);
$posters[] = [
'url' => DASHBOARD_NEWS_URL . rawurlencode($basename) . '?v=' . (int) filemtime($file),
'alt' => 'Aushang',
];
}
return array_values($posters);
}
function dashboard_month_images(): array
{
$images = [];
foreach ([1, 2] as $page) {
$filename = 'terminuebersicht-' . $page . '.png';
$path = __DIR__ . '/../month/' . $filename;
$version = is_file($path) ? (int) filemtime($path) : 0;
$images[] = [
'page' => $page,
'url' => 'month/' . $filename . '?v=' . $version,
];
}
return $images;
}
function dashboard_fetch_indoor_temperature(): array
{
$baseUrl = rtrim(intranet_env('HA_BASE_URL'), '/');
$token = intranet_env('HA_TOKEN');
$url = $baseUrl . '/api/states/' . rawurlencode(DASHBOARD_HA_ENTITY_ID);
$data = dashboard_http_json($url, ['Authorization: Bearer ' . $token]);
if (!array_key_exists('state', $data)) {
throw new RuntimeException('Unerwartete Home-Assistant-Antwort.');
}
$value = $data['state'];
if (is_numeric($value)) {
$value = round((float) $value, 1);
}
return [
'value' => $value,
'unit' => (string) ($data['attributes']['unit_of_measurement'] ?? '°C'),
];
}
function dashboard_fetch_outdoor_weather(): array
{
$query = http_build_query([
'lat' => DASHBOARD_OPENWEATHER_LATITUDE,
'lon' => DASHBOARD_OPENWEATHER_LONGITUDE,
'appid' => intranet_env('OPENWEATHER_API_KEY'),
'units' => 'metric',
'lang' => 'de',
], '', '&', PHP_QUERY_RFC3986);
$data = dashboard_http_json('https://api.openweathermap.org/data/2.5/weather?' . $query);
$temperature = $data['main']['temp'] ?? null;
$icon = (string) ($data['weather'][0]['icon'] ?? '');
if (!is_numeric($temperature)) {
throw new RuntimeException('Unerwartete OpenWeather-Antwort.');
}
return [
'temperature' => (int) round((float) $temperature),
'icon' => dashboard_weather_kind($icon),
];
}
function dashboard_weather_kind(string $icon): string
{
return match ($icon) {
'04d', '04n', '03d', '03n' => 'cloudy',
'02d' => 'cloudy_day',
'02n' => 'cloudy_night',
'01d' => 'sun',
'01n' => 'moon',
'09d', '09n', '10n' => 'rain_shower',
'10d' => 'rain',
'11d', '11n' => 'thunder',
'13d', '13n' => 'snowy',
'50d', '50n' => 'mist',
default => 'cloudy',
};
}
function dashboard_http_json(string $url, array $headers = []): array
{
$handle = curl_init($url);
if ($handle === false) {
throw new RuntimeException('HTTP-Anfrage konnte nicht initialisiert werden.');
}
curl_setopt_array($handle, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array_merge(['Accept: application/json'], $headers),
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
]);
try {
$response = curl_exec($handle);
$status = (int) curl_getinfo($handle, CURLINFO_HTTP_CODE);
$error = curl_error($handle);
} finally {
curl_close($handle);
}
if ($response === false || $status < 200 || $status >= 300) {
throw new RuntimeException($error !== '' ? $error : 'HTTP ' . $status);
}
try {
$data = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw new RuntimeException('Ungültige JSON-Antwort.', 0, $exception);
}
if (!is_array($data)) {
throw new RuntimeException('Unerwartete JSON-Antwort.');
}
return $data;
}