hier noch etwas überarbeitet und ohne Warnungen
// version: 0.2.6
//////////////////////////////////////////////////
const locationName = 'wo auch immer';
//////////////////////////////////////////////////
// Schriftgrößen-Konfiguration
const fontSizeCurrentLabel = '1.3rem';
const fontSizeTempBig = '3.8rem';
const fontSizeDescription = '1.1rem';
const fontSizeInfoGrid = '0.85rem';
const fontSizeSunMoon = '0.8rem';
const fontSizeHourly = '0.75rem';
const fontSizeHourlyRain = '0.65rem';
const fontSizeForecastDay = '0.75rem';
const fontSizeForecastText = '0.65rem';
const fontSizeForecastTempMax = '1rem';
const fontSizeForecastTempMin = '0.85rem';
const fontSizeForecastDetails = '0.65rem';
const version = '0.2.6';
const dpBase = 'open-meteo-weather.0.' + locationName + '.weather';
const forecast = dpBase + '.forecast';
const targetDP = '0_userdata.0.Wetter_Widget_HTML';
// Sprach-Check
let sysLang = 'de';
try {
const systemConfig = getObject("system.config");
sysLang = systemConfig.common.language || 'de';
} catch (e) { sysLang = 'de'; }
const i18n = {
de: { current: "Aktuell" },
en: { current: "Current" }
};
const lang = i18n[sysLang] || i18n['en'];
// DP anlegen falls nicht vorhanden
if (!existsState(targetDP)) {
createState(targetDP, '', {
name: 'Weather Widget for VIS',
type: 'string',
role: 'html'
});
}
// ------------------------------------------------------------
// Hilfsfunktionen
// ------------------------------------------------------------
// HEUTE aus JavaScript, kompakt
function getToday() {
const d = new Date();
const day = String(d.getDate()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, "0");
const year = d.getFullYear();
return `${day}.${month}.${year}`;
}
function getVal(id, unit = "") {
if (!existsState(id)) return "--" + unit;
let state = getState(id);
if (!state || state.val === null || state.val === undefined) return "--" + unit;
return state.val + unit;
}
function getImg(id, size = "20px") {
if (!existsState(id)) return "";
let state = getState(id);
if (!state || !state.val) return "";
return `<img src="${state.val}" style="width:${size}; height:${size}; object-fit:contain;">`;
}
function getNum(id) {
let s = getState(id);
return (!s || s.val === null || s.val === undefined) ? null : Number(s.val);
}
function getSunHours(id) {
let sec = getNum(id);
if (sec === null) return "--h";
return (sec / 3600).toFixed(1) + "h";
}
// ------------------------------------------------------------
// Hauptfunktion
// ------------------------------------------------------------
function updateWeatherWidget() {
// ⭐ AB HIER: ALLES UNVERÄNDERT GELASSEN
let html = `
<style>
@keyframes moonGlow {
0% { filter: drop-shadow(0 0 2px #fbbf24); opacity: 0.85; }
50% { filter: drop-shadow(0 0 6px #fbbf24); opacity: 1; }
100% { filter: drop-shadow(0 0 2px #fbbf24); opacity: 0.85; }
}
.moon-anim {
animation: moonGlow 3.5s ease-in-out infinite;
}
.w-container {
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(160deg, #1e293b 0%, #0f172a 100%);
color: #f1f5f9;
padding: 10px;
border-radius: 20px;
border: 1px solid #334155;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.w-header {
display: grid;
grid-template-columns: 1.2fr 1.5fr 1fr;
gap: 10px;
background: rgba(255,255,255,0.05);
padding: 10px;
border-radius: 10px;
margin-bottom: 10px;
border: 1px solid rgba(255,255,255,0.1);
}
.w-temp-big { font-size: ${fontSizeTempBig}; font-weight: 900; color: #fbbf24; line-height: 1; }
.w-desc { font-size: ${fontSizeDescription}; color: #38bdf8; font-weight: 600; }
.w-info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; font-size: ${fontSizeInfoGrid}; margin-top: 10px; }
.w-info-item { background: rgba(0,0,0,0.2); padding: 6px 10px; border-radius: 10px; display: flex; align-items: center; gap: 5px; }
.w-sun-moon { font-size: ${fontSizeSunMoon}; line-height: 1.6; border-left: 1px solid rgba(255,255,255,0.1); padding-left: 15px; }
.w-hourly {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 8px;
margin-bottom: 10px;
background: rgba(0,0,0,0.15);
padding: 10px;
border-radius: 15px;
}
.w-h-item { text-align: center; font-size: ${fontSizeHourly}; }
.w-h-time { font-weight: bold; color: #38bdf8; }
.w-h-temp { font-weight: bold; color: #fbbf24; display: block; }
.w-h-rain { font-size: ${fontSizeHourlyRain}; color: #94a3b8; }
.w-forecast {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 10px;
}
.w-fc-day {
background: rgba(255,255,255,0.03);
padding: 12px 8px;
border-radius: 18px;
text-align: center;
border: 1px solid rgba(255,255,255,0.05);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.w-fc-name {
font-weight: bold;
color: #38bdf8;
font-size: ${fontSizeForecastDay};
text-transform: uppercase;
margin-bottom: 2px;
}
.w-fc-text {
font-size: ${fontSizeForecastText};
color: #94a3b8;
min-height: 2.2em;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
margin-bottom: 5px;
}
.w-fc-temp-max {
color: #f87171;
font-weight: bold;
font-size: ${fontSizeForecastTempMax};
display: block;
text-align: center;
}
.w-fc-temp-min {
color: #60a5fa;
font-size: ${fontSizeForecastTempMin};
display: block;
margin-bottom: 5px;
text-align: center;
}
.w-fc-day img {
margin: 2px 0 4px 0;
}
.w-fc-details {
font-size: ${fontSizeForecastDetails};
color: #94a3b8;
border-top: 1px solid rgba(255,255,255,0.05);
margin-top: 8px;
padding-top: 8px;
}
</style>
<div class="w-container">
<div class="w-header">
<div style="text-align: center;">
<div style="font-size:${fontSizeCurrentLabel}; font-weight:bold;">
${lang.current} / ${getVal(forecast + '.day0.name_day')}
</div>
<div style="font-size:0.75rem; color:#94a3b8; margin-top:0;">
${getToday()}
</div>
${getImg(dpBase + '.current.icon_url', "80px")}
<div class="w-desc">${getVal(dpBase + '.current.weather_text')}</div>
</div>
<div>
<div class="w-temp-big">${getVal(dpBase + '.current.temperature_2m', " °C")}</div>
<div style="font-weight:bold; margin-bottom:10px;">
<span style="color:#f87171">${getVal(forecast + '.day0.temperature_2m_max', " °C")}</span> |
<span style="color:#60a5fa">${getVal(forecast + '.day0.temperature_2m_min', " °C")}</span>
</div>
<div class="w-info-grid">
<div class="w-info-item">💧 ${getVal(dpBase + '.current.relative_humidity_2m', "%")}</div>
<div class="w-info-item">🌧️ ${getVal(forecast + '.day0.precipitation_sum', "mm")}</div>
<div class="w-info-item">☀️ ${getSunHours(forecast + '.day0.sunshine_duration')}</div>
<div class="w-info-item">☁️ ${getVal(dpBase + '.current.cloud_cover', "%")}</div>
<div class="w-info-item">💨 Wind ${getVal(dpBase + '.current.wind_speed_10m', " km/h")}</div>
<div class="w-info-item">🌬️ Böen ${getVal(dpBase + '.current.wind_gusts_10m', " km/h")}</div>
</div>
</div>
<div class="w-sun-moon" style="position:relative;">
🌅 ${getVal(forecast + '.day0.sunrise')}<br>
🌇 ${getVal(forecast + '.day0.sunset')}<br>
🌙 ${getVal(forecast + '.day0.moonrise')}<br>
🌘 ${getVal(forecast + '.day0.moonset')}<br>
<div style="margin-top:6px;">
${getImg(forecast + '.day0.moon_phase_icon', "34px")}
<div class="moon-anim" style="font-size:0.75rem; color:#cbd5e1;">
${getVal(forecast + '.day0.moon_phase_text')}
</div>
</div>
<div style="margin-top:10px;">
💨 Richtung: ${getVal(dpBase + '.current.wind_direction_text')}<br>
${getImg(dpBase + '.current.wind_direction_icon', "30px")}
</div>
<div style="position:absolute; bottom:-10px; right:0; font-size:0.6rem; color:#475569; opacity:0.8;">
Script Version ${version}
</div>
</div>
</div>
<div class="w-hourly">
`;
// Stunden – mit Wind + Mond
for (let h = 0; h <= 5; h++) {
let hPath = forecast + '.hourly.next_hours.hour' + h;
let isNight = getNum(hPath + '.is_day') === 0;
html += `
<div class="w-h-item">
<div class="w-h-time">${getVal(hPath + '.time')}</div>
${getImg(hPath + '.icon_url', "30px")}
<span class="w-h-temp">${getVal(hPath + '.temperature_2m', " °C")}</span>
<span class="w-h-rain">
🌧️${getVal(hPath + '.precipitation_probability', "%")} /
${getVal(hPath + '.precipitation', "mm")}
</span>
<div style="margin-top:4px; font-size:0.7rem; color:#cbd5e1;">
💨 ${getVal(hPath + '.wind_speed_10m', " km/h")}
🌬️ ${getVal(hPath + '.wind_gusts_10m', " km/h")}
</div>
<div style="margin-top:2px;">
${getImg(hPath + '.wind_direction_icon', "32px")}
<span style="font-size:0.7rem; color:#94a3b8;">
${getVal(hPath + '.wind_direction_text')}
</span>
</div>
${
isNight
? `
<div style="margin-top:4px;">
${getImg(hPath + '.moon_phase_icon', "24px")}
<div style="font-size:0.65rem; color:#cbd5e1;">
${getVal(hPath + '.moon_phase_text')}
</div>
</div>
`
: ""
}
</div>
`;
}
html += `
</div>
<div class="w-forecast">
`;
// 6-Tage-Vorhersage
for (let i = 1; i <= 6; i++) {
let d = forecast + '.day' + i;
html += `
<div class="w-fc-day">
<div>
<div class="w-fc-name">${getVal(d + '.name_day')}</div>
<div class="w-fc-text">${getVal(d + '.weather_text')}</div>
${getImg(d + '.icon_url', "50px")}
<span class="w-fc-temp-max">${getVal(d + '.temperature_2m_max', " °C")}</span>
<span class="w-fc-temp-min">${getVal(d + '.temperature_2m_min', " °C")}</span>
</div>
<div class="w-fc-details">
🌧️ ${getVal(d + '.precipitation_sum', "mm")} (${getVal(d + '.precipitation_probability_max', "%")})<br>
💧 ${getVal(d + '.relative_humidity_2m_mean', "%")}<br>
💨 Wind ${getVal(d + '.wind_speed_10m_max', " km/h")}<br>
🌬️ Böen ${getVal(d + '.wind_gusts_10m_max', " km/h")}<br>
${getVal(d + '.wind_direction_text')}<br>
${getImg(d + '.wind_direction_icon', "32px")}<br>
${getImg(d + '.wind_gust_icon', "24px")}<br>
☀️ ${getSunHours(d + '.sunshine_duration')}
</div>
</div>
`;
}
html += `</div></div>`;
setState(targetDP, html, true);
console.log("Weather widget: HTML successfully generated (v0.2.6-XYZ-SUN-CLOUD-C-DATE-COMPACT).");
}
// Trigger
updateWeatherWidget();
schedule("*/5 * * * *", updateWeatherWidget);
on({id: dpBase + '.current.temperature_2m', change: 'any'}, updateWeatherWidget);
on({id: forecast + '.hourly.next_hours.hour0.time', change: 'any'}, updateWeatherWidget);
[image: 1770566801260-43377c6a-c836-4580-8f67-56ca3bac553b-image.png]