Metainformationen zur Seite
  •  

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
iobroker:countdown_script_fuer_vis1 [06.01.2026] – koljaiobroker:countdown_script_fuer_vis1 [12.10.2026] (aktuell) – gelöscht kolja
Zeile 1: Zeile 1:
-🔔 Countdown-Widget (ioBroker VIS + MagicMirror) 
  
-Dieses Widget zeigt automatisch die zwei nächsten anstehenden Countdowns an 
-(z. B. Weihnachten, Silvester, Urlaub, Touren usw.). 
- 
-Abgelaufene Countdowns werden automatisch ausgeblendet. 
- 
-⚠️ WICHTIGE HINWEISE (bitte lesen!) 
- 
-JEDER Countdown muss im versteckten Subscribe-Block stehen 
- 
-KEINE Einzeiler mit { ... } im JavaScript 
- 
-Kein Array / kein Objekt-Literal 
- 
-Code-Formatierung NICHT verändern 
- 
-Sonst erscheint: 
- 
-SyntaxError: Unexpected token 'null' 
- 
-🧩 1. Subscribe-Block (Pflicht!) 
- 
-Hier meldet VIS alle benötigten Datenpunkte an. 
- 
-<div style="display:none;"> 
-  {countdown.0.countdowns.Weihnachten.fullJson} 
-  {countdown.0.countdowns.Silvester.fullJson} 
-  {countdown.0.countdowns.SommerurlaubItalien.fullJson} 
-</div> 
- 
- 
-👉 Für jeden neuen Countdown MUSS hier eine Zeile ergänzt werden! 
- 
-🖼️ 2. Anzeige-HTML (2 Slots) 
-<div style="text-align:center;"> 
-  <div id="cd_text_xmas" style="font-size:28px;font-weight:700;line-height:1.15;"> 
-    Lade... 
-  </div> 
- 
-  <div style="height:18px;"></div> 
- 
-  <div id="cd_text_ny" style="font-size:28px;font-weight:700;line-height:1.15;"> 
-    Lade... 
-  </div> 
-</div> 
- 
- 
-Die IDs bleiben absichtlich so – sie sind MagicMirror-erprobt. 
- 
-🧠 3. JavaScript – Finale Version 
- 
-⚠️ NICHT kürzen, NICHT umformatieren 
-<code> 
- 
-<!-- ========================================================== 
-     VIS Subscribe-Block (Pflicht!) 
-     Jede OID, die du unten nutzt, MUSS hier stehen. 
-     ========================================================== --> 
-<div style="display:none;"> 
-  {countdown.0.countdowns.Weihnachten.fullJson} 
-  {countdown.0.countdowns.Silvester.fullJson} 
-  {countdown.0.countdowns.SommerurlaubItalien.fullJson} 
-</div> 
- 
-<div style="text-align:center;"> 
-  <div id="cd_text_xmas" style="font-size:28px;font-weight:700;line-height:1.15;"> 
-    Lade... 
-  </div> 
- 
-  <div style="height:18px;"></div> 
- 
-  <div id="cd_text_ny" style="font-size:28px;font-weight:700;line-height:1.15;"> 
-    Lade... 
-  </div> 
-</div> 
- 
-<script> 
-(function () 
-{ 
-  /* ========================================================== 
-     KONFIGURATION 
-     ========================================================== */ 
- 
-  const EMPTY_TEXT = "…"; 
- 
-  // Wenn TRUE: Stunden werden ausgeblendet, sobald days > 1 
-  // (für days === 1 werden Stunden weiterhin angezeigt) 
-  const HIDE_HOURS_IF_MORE_THAN_ONE_DAY = true; 
- 
-  // Wieviele Events sind AKTIV konfiguriert (max 8) 
-  const EVENT_COUNT = 3; 
- 
-  /* ========================================================== 
-     EVENT-DEFINITIONEN 
-     TYPE: 
-       1 = Weihnachten (Speziallogik) 
-       2 = Silvester   (Speziallogik) 
-       3 = Generisch   (Titel + Emoji) 
-     ========================================================== */ 
- 
-  // ---------------- Event 1 ---------------- 
-  const E1_OID   = "countdown.0.countdowns.Weihnachten.fullJson"; 
-  const E1_TYPE  = 1; 
-  const E1_TITLE = ""; 
-  const E1_EMOJI = ""; 
- 
-  // ---------------- Event 2 ---------------- 
-  const E2_OID   = "countdown.0.countdowns.Silvester.fullJson"; 
-  const E2_TYPE  = 2; 
-  const E2_TITLE = ""; 
-  const E2_EMOJI = ""; 
- 
-  // ---------------- Event 3 ---------------- 
-  const E3_OID   = "countdown.0.countdowns.SommerurlaubItalien.fullJson"; 
-  const E3_TYPE  = 3; 
-  const E3_TITLE = "Sommerurlaub Italien"; 
-  const E3_EMOJI = "☀️🇮🇹"; 
- 
-  // ---------------- Event 4..8 (Vorlagen) ---------------- 
-  const E4_OID   = ""; 
-  const E4_TYPE  = 3; 
-  const E4_TITLE = ""; 
-  const E4_EMOJI = ""; 
- 
-  const E5_OID   = ""; 
-  const E5_TYPE  = 3; 
-  const E5_TITLE = ""; 
-  const E5_EMOJI = ""; 
- 
-  const E6_OID   = ""; 
-  const E6_TYPE  = 3; 
-  const E6_TITLE = ""; 
-  const E6_EMOJI = ""; 
- 
-  const E7_OID   = ""; 
-  const E7_TYPE  = 3; 
-  const E7_TITLE = ""; 
-  const E7_EMOJI = ""; 
- 
-  const E8_OID   = ""; 
-  const E8_TYPE  = 3; 
-  const E8_TITLE = ""; 
-  const E8_EMOJI = ""; 
- 
-  /* ========================================================== 
-     HILFSFUNKTIONEN (VIS-safe formatiert) 
-     ========================================================== */ 
- 
-  function safeNum(x, fallback) 
-  { 
-    const n = Number(x); 
- 
-    if (Number.isFinite(n)) 
-    { 
-      return n; 
-    } 
- 
-    return fallback; 
-  } 
- 
-  function parseFullJson(val) 
-  { 
-    if (val === null || val === undefined || val === "") 
-    { 
-      return null; 
-    } 
- 
-    try 
-    { 
-      let t = val; 
- 
-      if (typeof t === "string") 
-      { 
-        t = t.trim(); 
- 
-        if (t.startsWith('"') && t.endsWith('"')) 
-        { 
-          t = JSON.parse(t); 
-        } 
- 
-        return JSON.parse(t); 
-      } 
- 
-      return t; 
-    } 
-    catch (e) 
-    { 
-      return null; 
-    } 
-  } 
- 
-  function subline(s) 
-  { 
-    return "<br><span style='font-size:18px;font-weight:400;opacity:.85'>(noch " + s + ")</span>"; 
-  } 
- 
-  function dayWord(n) 
-  { 
-    if (n === 1) 
-    { 
-      return "Tag"; 
-    } 
- 
-    return "Tagen"; 
-  } 
- 
-  function hourWord(n) 
-  { 
-    if (n === 1) 
-    { 
-      return "Stunde"; 
-    } 
- 
-    return "Stunden"; 
-  } 
- 
-  /* ========================================================== 
-     EVENT-ZUGRIFF (ohne Arrays/Objekte) 
-     ========================================================== */ 
- 
-  function getOid(i) 
-  { 
-    if (i === 1) return E1_OID; 
-    if (i === 2) return E2_OID; 
-    if (i === 3) return E3_OID; 
-    if (i === 4) return E4_OID; 
-    if (i === 5) return E5_OID; 
-    if (i === 6) return E6_OID; 
-    if (i === 7) return E7_OID; 
-    if (i === 8) return E8_OID; 
- 
-    return ""; 
-  } 
- 
-  function getType(i) 
-  { 
-    if (i === 1) return E1_TYPE; 
-    if (i === 2) return E2_TYPE; 
-    if (i === 3) return E3_TYPE; 
-    if (i === 4) return E4_TYPE; 
-    if (i === 5) return E5_TYPE; 
-    if (i === 6) return E6_TYPE; 
-    if (i === 7) return E7_TYPE; 
-    if (i === 8) return E8_TYPE; 
- 
-    return 0; 
-  } 
- 
-  function getTitle(i) 
-  { 
-    if (i === 1) return E1_TITLE; 
-    if (i === 2) return E2_TITLE; 
-    if (i === 3) return E3_TITLE; 
-    if (i === 4) return E4_TITLE; 
-    if (i === 5) return E5_TITLE; 
-    if (i === 6) return E6_TITLE; 
-    if (i === 7) return E7_TITLE; 
-    if (i === 8) return E8_TITLE; 
- 
-    return ""; 
-  } 
- 
-  function getEmoji(i) 
-  { 
-    if (i === 1) return E1_EMOJI; 
-    if (i === 2) return E2_EMOJI; 
-    if (i === 3) return E3_EMOJI; 
-    if (i === 4) return E4_EMOJI; 
-    if (i === 5) return E5_EMOJI; 
-    if (i === 6) return E6_EMOJI; 
-    if (i === 7) return E7_EMOJI; 
-    if (i === 8) return E8_EMOJI; 
- 
-    return ""; 
-  } 
- 
-  /* ========================================================== 
-     RESTZEIT / ABGELAUFEN 
-     ========================================================== */ 
- 
-  function remainingSeconds(val) 
-  { 
-    const d = parseFullJson(val); 
- 
-    if (!d) 
-    { 
-      return 999999999; 
-    } 
- 
-    if (d.total && d.total.seconds !== undefined) 
-    { 
-      return safeNum(d.total.seconds, 999999999); 
-    } 
- 
-    const days  = safeNum((d.total && d.total.days  !== undefined) ? d.total.days  : d.days, 0); 
-    const hours = safeNum((d.total && d.total.hours !== undefined) ? d.total.hours : d.hours, 0); 
-    const mins  = safeNum((d.total && d.total.minutes !== undefined) ? d.total.minutes : d.minutes, 0); 
-    const secs  = safeNum((d.total && d.total.seconds !== undefined) ? d.total.seconds : d.seconds, 0); 
- 
-    return (days * 86400) + (hours * 3600) + (mins * 60) + secs; 
-  } 
- 
-  function isExpired(val) 
-  { 
-    return (remainingSeconds(val) <= 0); 
-  } 
- 
-  /* ========================================================== 
-     FORMATTER: Zeittext mit optionalen Stunden 
-     ---------------------------------------------------------- 
-     Gibt nur den "Zeitanteil" zurück, z.B.: 
-       - "3 Tagen" (wenn days>1 und hideHours aktiv) 
-       - "1 Tag und 5 Stunden" 
-     ========================================================== */ 
- 
-  function formatDaysHours(days, hours) 
-  { 
-    // Wenn konfiguriert: Stunden ausblenden sobald days > 1 
-    if (HIDE_HOURS_IF_MORE_THAN_ONE_DAY) 
-    { 
-      if (days > 1) 
-      { 
-        return String(days) + " " + dayWord(days); 
-      } 
-    } 
- 
-    // Standard: Tage + Stunden 
-    return String(days) + " " + dayWord(days) + " und " + String(hours) + " " + hourWord(hours); 
-  } 
- 
-  /* ========================================================== 
-     RENDERER 
-     ========================================================== */ 
- 
-  function renderWeihnachten(val) 
-  { 
-    const data = parseFullJson(val); 
-    const now  = new Date(); 
- 
-    if (now.getMonth() === 11 && now.getDate() >= 24 && now.getDate() <= 26) 
-    { 
-      return "🎄 Frohe Weihnachten 🎄<br><span style='font-size:26px;'>🎅🛷🎁</span>"; 
-    } 
- 
-    if (!data) 
-    { 
-      return EMPTY_TEXT; 
-    } 
- 
-    const days  = safeNum((data.total && data.total.days !== undefined) ? data.total.days : data.days, 0); 
-    const hours = safeNum((data.hours !== undefined) ? data.hours : (data.total ? data.total.hours : undefined), 0); 
-    const words = (data.inWords && data.inWords.long) ? data.inWords.long : ""; 
- 
-    if (now.getMonth() === 11 && now.getDate() === 23) 
-    { 
-      return "🎅 Weihnachten morgen" + subline(words); 
-    } 
- 
-    if (days >= 1) 
-    { 
-      return "🎁 Weihnachten in " + formatDaysHours(days, hours); 
-    } 
- 
-    return "🎄 Frohe Weihnachten 🎄"; 
-  } 
- 
-  function renderSilvester(val) 
-  { 
-    const data = parseFullJson(val); 
-    const now  = new Date(); 
- 
-    if (now.getMonth() === 11 && now.getDate() === 31) 
-    { 
-      return "🎇 Silvester heute"; 
-    } 
- 
-    if (now.getMonth() === 0 && now.getDate() === 1) 
-    { 
-      return "🥂🎉 Frohes neues Jahr! 🎉🥂"; 
-    } 
- 
-    if (!data) 
-    { 
-      return EMPTY_TEXT; 
-    } 
- 
-    const days  = safeNum((data.total && data.total.days !== undefined) ? data.total.days : data.days, 0); 
-    const hours = safeNum((data.hours !== undefined) ? data.hours : (data.total ? data.total.hours : undefined), 0); 
-    const words = (data.inWords && data.inWords.long) ? data.inWords.long : ""; 
- 
-    if (now.getMonth() === 11 && now.getDate() === 30) 
-    { 
-      return "🎆 Silvester morgen" + subline(words); 
-    } 
- 
-    if (days >= 1) 
-    { 
-      return "🕛 Silvester in " + formatDaysHours(days, hours); 
-    } 
- 
-    return "🎇 Silvester heute" + subline(words); 
-  } 
- 
-  function renderGeneric(val, title, emoji) 
-  { 
-    const data = parseFullJson(val); 
- 
-    if (!data) 
-    { 
-      return EMPTY_TEXT; 
-    } 
- 
-    const days  = safeNum((data.total && data.total.days !== undefined) ? data.total.days : data.days, 0); 
-    const hours = safeNum((data.hours !== undefined) ? data.hours : (data.total ? data.total.hours : undefined), 0); 
-    const words = (data.inWords && data.inWords.long) ? data.inWords.long : ""; 
- 
-    if (days === 1) 
-    { 
-      return emoji + " " + title + " morgen" + subline(words); 
-    } 
- 
-    if (days >= 1) 
-    { 
-      return emoji + " " + title + " in " + formatDaysHours(days, hours); 
-    } 
- 
-    if (words) 
-    { 
-      return emoji + " " + title + subline(words); 
-    } 
- 
-    return emoji + " " + title + " bald"; 
-  } 
- 
-  function renderByIndex(i, val) 
-  { 
-    const t = getType(i); 
- 
-    if (t === 1) 
-    { 
-      return renderWeihnachten(val); 
-    } 
- 
-    if (t === 2) 
-    { 
-      return renderSilvester(val); 
-    } 
- 
-    return renderGeneric(val, getTitle(i), getEmoji(i)); 
-  } 
- 
-  /* ========================================================== 
-     AUSWAHL: 2 nächste Events 
-     ========================================================== */ 
- 
-  function pickTwoUpcoming() 
-  { 
-    let best1_i = -1; 
-    let best1_r = 999999999; 
- 
-    let best2_i = -1; 
-    let best2_r = 999999999; 
- 
-    let i = 1; 
- 
-    while (i <= EVENT_COUNT) 
-    { 
-      const oid = getOid(i); 
- 
-      if (oid) 
-      { 
-        const val = vis.states.attr(oid + ".val"); 
- 
-        if (!isExpired(val)) 
-        { 
-          const r = remainingSeconds(val); 
- 
-          if (r < best1_r) 
-          { 
-            best2_i = best1_i; 
-            best2_r = best1_r; 
- 
-            best1_i = i; 
-            best1_r = r; 
-          } 
-          else 
-          { 
-            if (r < best2_r) 
-            { 
-              best2_i = i; 
-              best2_r = r; 
-            } 
-          } 
-        } 
-      } 
- 
-      i = i + 1; 
-    } 
- 
-    return String(best1_i) + "|" + String(best2_i); 
-  } 
- 
-  function renderSlot(slotIndex, el) 
-  { 
-    const picks = pickTwoUpcoming().split("|"); 
-    let idx = -1; 
- 
-    if (slotIndex === 1) 
-    { 
-      idx = Number(picks[0]); 
-    } 
-    else 
-    { 
-      idx = Number(picks[1]); 
-    } 
- 
-    if (idx < 1) 
-    { 
-      el.innerHTML = EMPTY_TEXT; 
-      return; 
-    } 
- 
-    const oid = getOid(idx); 
-    const val = vis.states.attr(oid + ".val"); 
- 
-    el.innerHTML = renderByIndex(idx, val); 
-  } 
- 
-  /* ========================================================== 
-     START & BINDING 
-     ========================================================== */ 
- 
-  function start() 
-  { 
-    const el1 = document.getElementById("cd_text_xmas"); 
-    const el2 = document.getElementById("cd_text_ny"); 
- 
-    if (typeof vis !== "undefined" && vis.states && el1 && el2) 
-    { 
-      function updateSlot1() 
-      { 
-        renderSlot(1, el1); 
-      } 
- 
-      function updateSlot2() 
-      { 
-        renderSlot(2, el2); 
-      } 
- 
-      if (E1_OID) vis.states.bind(E1_OID + ".val", updateSlot1); 
-      if (E2_OID) vis.states.bind(E2_OID + ".val", updateSlot1); 
-      if (E3_OID) vis.states.bind(E3_OID + ".val", updateSlot1); 
-      if (E4_OID) vis.states.bind(E4_OID + ".val", updateSlot1); 
-      if (E5_OID) vis.states.bind(E5_OID + ".val", updateSlot1); 
-      if (E6_OID) vis.states.bind(E6_OID + ".val", updateSlot1); 
-      if (E7_OID) vis.states.bind(E7_OID + ".val", updateSlot1); 
-      if (E8_OID) vis.states.bind(E8_OID + ".val", updateSlot1); 
- 
-      if (E1_OID) vis.states.bind(E1_OID + ".val", updateSlot2); 
-      if (E2_OID) vis.states.bind(E2_OID + ".val", updateSlot2); 
-      if (E3_OID) vis.states.bind(E3_OID + ".val", updateSlot2); 
-      if (E4_OID) vis.states.bind(E4_OID + ".val", updateSlot2); 
-      if (E5_OID) vis.states.bind(E5_OID + ".val", updateSlot2); 
-      if (E6_OID) vis.states.bind(E6_OID + ".val", updateSlot2); 
-      if (E7_OID) vis.states.bind(E7_OID + ".val", updateSlot2); 
-      if (E8_OID) vis.states.bind(E8_OID + ".val", updateSlot2); 
- 
-      updateSlot1(); 
-      updateSlot2(); 
-    } 
-    else 
-    { 
-      setTimeout(start, 300); 
-    } 
-  } 
- 
-  if (document.readyState === "complete") 
-  { 
-    start(); 
-  } 
-  else 
-  { 
-    window.addEventListener("load", start); 
-  } 
- 
-})(); 
-</script> 
- 
- 
-</code> 
- 
-➕ Neuen Countdown hinzufügen (Checkliste) 
-Beispiel: countdown.0.countdowns.Herbsturlaub.fullJson 
-✅ Schritt 1 – Subscribe-Block 
-{countdown.0.countdowns.Herbsturlaub.fullJson} 
- 
-✅ Schritt 2 – OID definieren 
-const OID_4 = "countdown.0.countdowns.Herbsturlaub.fullJson"; 
- 
-✅ Schritt 3 – Renderfunktion ergänzen 
-if (i === 4) return renderGeneric(val, "Herbsturlaub", "🍂🧳"); 
- 
-✅ Schritt 4 – remainingSeconds / pickTwo erweitern 
- 
-(analog zu Event 3)