Skip to content
  • Recent
  • Tags
  • 0 Unread 0
  • Categories
  • Unreplied
  • Popular
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Logo
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. Eigene Widget mit HTML/CSS/JAVASCRIPT + AI

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.1k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    1.8k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    15
    1
    2.1k

Eigene Widget mit HTML/CSS/JAVASCRIPT + AI

Scheduled Pinned Locked Moved Skripten / Logik
2 Posts 2 Posters 209 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    smartysmart
    wrote on last edited by
    #1

    Hey Leute,
    mir fehlen leider die Programmierkentnnise um das HTML Widget selber zu machen also habe ich mir mit Claude.ai etwas Hilfe geholt.

    Ich bin auch fast am Ziel meines Vorhabens allerdings bekomme ich einen Fehler seit Stunden nicht heraus.

    Ich habe mir ein Widget gemacht wo ich Jalousien direkt hoch und runter fahren kann. Und links davon der aktuelle Status von der Jalousie angezeigt werden soll
    ab1d8a68-cfc6-4ab4-a7d0-d280c0a7da5e-image.png
    Hier ist bereits der erste Fehler weil da 0% angezeigt wird statt der echte Wert.

    Wenn ich auf das Widget klicke wird ein PopUp geöffnet mit mehr Funktionen, auch hier werden die aktuellen Werte nicht angezeigt.
    237926cc-d26e-41d9-a35a-0c3f7266ee23-image.png

    Der Regler funktioniert und die Prozente werden beim bedienen angezeigt,
    5045dbd3-7d92-4d87-b56b-0a1c45356a0e-image.png allerdings nur solange das PopUp offen ist. sobal ich das schliese und neu öffne steht oben wieder 0

    P.S im Editor wird die Jalousien Position agezeigt
    529238eb-dc74-4794-8b44-0555e47a277e-image.png

    Ich gehe aktuell also davon aus, dass auch die Werte im PopUp im Editor angezeigt werden und das das Problem nur in der VIS Ansicht ist.

    Das ist der Code dazu

    <style>
      .jalousie-widget-container {
        width: 100%;
        height: 100%;
        box-sizing: border-box;
        border-radius: 20px;
        padding: 0;
        background: #f5f5f5;
        font-family: sans-serif;
        position: relative;
        margin: 0 auto;
        border: 1px solid #e0e0e0;
        display: flex;
        flex-direction: column;
      }
      
      .jalousie-widget-header {
        display: flex;
        align-items: center;
        padding: calc(min(10px, 3%));
        border-bottom: 1px dotted #ccc;
        flex: 0 0 auto;
      }
      
      .jalousie-widget-icon {
        width: calc(min(24px, 15%));
        height: calc(min(24px, 15%));
        margin-right: calc(min(10px, 3%));
        min-width: 16px;
        min-height: 16px;
      }
      
      .jalousie-widget-title {
        font-size: calc(min(14px, 4vw));
        margin: 0;
        padding: calc(min(8px, 3%)) 0;
        text-align: left;
        border-bottom: 1px dotted #ccc;
        padding-left: calc(min(10px, 3%));
        flex: 0 0 auto;
      }
      
      .jalousie-widget-status {
        display: flex;
        align-items: center;
        padding: calc(min(10px, 3%)) calc(min(12px, 4%));
        justify-content: space-between;
        flex: 1 0 auto;
      }
      
      .jalousie-status-text {
        font-size: calc(min(16px, 4.5vw));
        margin: 0;
        font-weight: 500;
      }
      
      .jalousie-widget-buttons {
        display: flex;
      }
    
      .jalousie-widget-button {
        width: calc(min(44px, 35%));
        height: calc(min(44px, 35%));
        display: flex;
        align-items: center;
        justify-content: center;
        border: 1px solid #ccc;
        background: white;
        border-radius: 50%;
        margin-left: 8px;
        cursor: pointer;
        font-size: calc(min(26px, 8vw));
        padding: 0;
        min-width: 40px;
        min-height: 40px;
        font-weight: bold;
        color: #333;
      }
    
      .jalousie-popup-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.5);
        display: none;
        z-index: 9998;
        touch-action: none;
      }
    
      .jalousie-popup {
        display: none;
        position: fixed;
        top: 5%;
        left: 5%;
        width: 90%;
        height: 90%;
        max-height: 90vh;
        background: white;
        border-radius: 8px;
        box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
        z-index: 9999;
        font-family: sans-serif;
        overflow: hidden;
        touch-action: none;
      }
      
      .jalousie-popup.active {
        display: flex;
        flex-direction: column;
      }
      
      .jalousie-popup-header {
        padding: 15px;
        border-bottom: 1px solid #eee;
        font-size: 18px;
        font-weight: 500;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
      
      .jalousie-popup-status {
        padding: 15px;
        border-bottom: 1px solid #eee;
        display: flex;
        justify-content: space-between;
        align-items: center;
        background: #f9f9f9;
      }
      
      .jalousie-popup-status-item {
        display: flex;
        align-items: center;
      }
      
      .jalousie-popup-status-label {
        font-size: 14px;
        color: #666;
        margin-right: 10px;
      }
      
      .jalousie-popup-status-value {
        font-size: 16px;
        font-weight: bold;
      }
      
      .jalousie-popup-content {
        flex: 1;
        display: flex;
        padding: 20px;
        justify-content: center;
        overflow: hidden;
        touch-action: none;
      }
      
      .jalousie-sliders-container {
        display: flex;
        width: 100%;
        max-width: 600px;
        justify-content: space-around;
        align-items: flex-start;
      }
      
      .jalousie-slider-column {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 45%;
      }
      
      .jalousie-slider-wrapper {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 100%;
        height: 100%;
        max-height: 500px;
      }
      
      .jalousie-slider-icon {
        margin-bottom: 10px;
        width: 24px;
        height: 24px;
      }
      
      .jalousie-slider-vertical {
        -webkit-appearance: slider-vertical;
        appearance: slider-vertical;
        height: 80%;
        min-height: 200px;
        width: 50px;
        margin: 20px 0;
        transform: rotate(180deg);
        background: #e0e0e0;
        border-radius: 25px;
        outline: none;
        cursor: pointer;
      }
      
      .jalousie-slider-vertical::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 60px;
        height: 60px;
        background: #007bff;
        border-radius: 50%;
        cursor: pointer;
        box-shadow: 0 0 5px rgba(0,0,0,0.2);
      }
      
      .jalousie-slider-vertical::-moz-range-thumb {
        width: 60px;
        height: 60px;
        background: #007bff;
        border-radius: 50%;
        cursor: pointer;
        box-shadow: 0 0 5px rgba(0,0,0,0.2);
        border: none;
      }
      
      .jalousie-slider-label {
        font-size: 16px;
        margin-top: 10px;
        text-align: center;
      }
      
      .jalousie-slider-buttons {
        display: flex;
        gap: 10px;
        margin-top: 15px;
      }
      
      .jalousie-slider-button {
        width: 50px;
        height: 50px;
        display: flex;
        align-items: center;
        justify-content: center;
        border: 1px solid #ccc;
        background: white;
        border-radius: 50%;
        cursor: pointer;
        font-size: 24px;
        padding: 0;
        font-weight: bold;
        touch-action: manipulation;
        color: #333;
      }
      
      .jalousie-slider-button:hover {
        background: #f5f5f5;
      }
      
      .jalousie-slider-button:active {
        background: #e0e0e0;
      }
      
      .jalousie-popup-footer {
        padding: 15px;
        border-top: 1px solid #eee;
        display: flex;
        justify-content: space-between;
      }
      
      .jalousie-popup-button {
        padding: 10px 20px;
        background: #f5f5f5;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
        cursor: pointer;
        color: #333;
      }
      
      .jalousie-popup-button:hover {
        background: #eee;
      }
      
      .jalousie-value-display {
        font-size: 20px;
        font-weight: bold;
        margin-top: 10px;
        margin-bottom: 20px;
      }
    </style>
    
    <div class="jalousie-widget-container" id="dg-buero-jalousie-widget">
      <div class="jalousie-widget-header">
        <svg id="dg-buero-jalousie-icon" class="jalousie-widget-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="cursor: pointer;">
          <rect x="4" y="4" width="16" height="16" rx="2" />
          <line x1="4" y1="12" x2="20" y2="12" />
          <line x1="7" y1="8" x2="17" y2="8" />
          <line x1="7" y1="16" x2="17" y2="16" />
        </svg>
      </div>
      
      <div class="jalousie-widget-title">
        DG Büro
      </div>
      
      <div class="jalousie-widget-status">
        <div class="jalousie-status-text">
          <span id="dg-buero-jalousie-status">0</span>%
        </div>
        
        <div class="jalousie-widget-buttons">
          <button id="dg-buero-btn-up" class="jalousie-widget-button">↑</button>
          <button id="dg-buero-btn-down" class="jalousie-widget-button">↓</button>
        </div>
      </div>
    </div>
    
    <div id="dg-buero-jalousie-popup-overlay" class="jalousie-popup-overlay"></div>
    <div id="dg-buero-jalousie-popup" class="jalousie-popup">
      <div class="jalousie-popup-header">
        DG Büro
        <button id="dg-buero-close-popup" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #333;">×</button>
      </div>
      
      <div class="jalousie-popup-status">
        <div class="jalousie-popup-status-item">
          <span class="jalousie-popup-status-label">Jalousie:</span>
          <span class="jalousie-popup-status-value" id="dg-buero-popup-status-jalousie">0%</span>
        </div>
        <div class="jalousie-popup-status-item">
          <span class="jalousie-popup-status-label">Lamellen:</span>
          <span class="jalousie-popup-status-value" id="dg-buero-popup-status-lamellen">0%</span>
        </div>
      </div>
      
      <div class="jalousie-popup-content">
        <div class="jalousie-sliders-container">
          <div class="jalousie-slider-column">
            <div class="jalousie-slider-wrapper">
              <svg class="jalousie-slider-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <rect x="6" y="4" width="12" height="16" rx="1" />
                <line x1="6" y1="8" x2="18" y2="8" />
                <line x1="6" y1="12" x2="18" y2="12" />
                <line x1="6" y1="16" x2="18" y2="16" />
              </svg>
              <div class="jalousie-value-display"><span id="dg-buero-popup-jalousie-value">0</span>%</div>
              <input type="range" min="0" max="100" value="0" id="dg-buero-slider-jalousie" class="jalousie-slider-vertical" orient="vertical" />
              <div class="jalousie-slider-label">Position Jalousie</div>
              <div class="jalousie-slider-buttons">
                <button id="dg-buero-btn-jalousie-up" class="jalousie-slider-button">↑</button>
                <button id="dg-buero-btn-jalousie-down" class="jalousie-slider-button">↓</button>
              </div>
            </div>
          </div>
          
          <div class="jalousie-slider-column">
            <div class="jalousie-slider-wrapper">
              <svg class="jalousie-slider-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M12 3 L4 10 L20 10 Z" />
                <path d="M12 10 L4 17 L20 17 Z" />
              </svg>
              <div class="jalousie-value-display"><span id="dg-buero-popup-lamellen-value">0</span>%</div>
              <input type="range" min="0" max="100" value="0" id="dg-buero-slider-lamellen" class="jalousie-slider-vertical" orient="vertical" />
              <div class="jalousie-slider-label">Position Lamellen</div>
              <div class="jalousie-slider-buttons">
                <button id="dg-buero-btn-lamellen-up" class="jalousie-slider-button">↑</button>
                <button id="dg-buero-btn-lamellen-down" class="jalousie-slider-button">↓</button>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <div class="jalousie-popup-footer">
        <button id="dg-buero-btn-stop" class="jalousie-popup-button">STOP</button>
        <div>
          <button id="dg-buero-btn-preset-1" class="jalousie-popup-button">50%</button>
          <button id="dg-buero-btn-preset-2" class="jalousie-popup-button">100%</button>
        </div>
      </div>
    </div>
    
    <script>
      // Datenpunkte definieren - Diese müssen an dein ioBroker-System angepasst sein
      const statusDP = 'openknx.0.Status.Jalousinen_Lamellen.Position_Jalousine_DG_Büro_status';  // For main widget display
      const statusJalousieDP = 'openknx.0.Status.Jalousinen_Lamellen.Position_Jalousine_DG_Büro_status';  // Jalousie status in popup
      const statusLamellenDP = 'openknx.0.Status.Jalousinen_Lamellen.Position_Lamellen_DG_Büro_status';  // Lamella status in popup
      const lamellenDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Balkon_Lamellen';
      const jalousienDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Balkon_Jalousien';
      const absoluteJalousieDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Absolute_Position_Jalousine';
      const absoluteLamellenDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Absolute_Position_Lamellen';
    
      let holdTimeout;
      let statusValue = 0;
      let jalousieValue = 0;  // Status value for jalousie
      let lamellenValue = 0;  // Status value for lamella
    
      // DOM-Elemente
      const btnUp = document.getElementById('dg-buero-btn-up');
      const btnDown = document.getElementById('dg-buero-btn-down');
      const jalousieIcon = document.getElementById('dg-buero-jalousie-icon');
      const closePopup = document.getElementById('dg-buero-close-popup');
      const jalousiePopup = document.getElementById('dg-buero-jalousie-popup');
      const popupOverlay = document.getElementById('dg-buero-jalousie-popup-overlay');
      const statusElement = document.getElementById('dg-buero-jalousie-status');
      const sliderJalousie = document.getElementById('dg-buero-slider-jalousie');
      const sliderLamellen = document.getElementById('dg-buero-slider-lamellen');
      
      // DOM-Elemente für die Popup-Werte
      const popupJalousieValue = document.getElementById('dg-buero-popup-jalousie-value');
      const popupLamellenValue = document.getElementById('dg-buero-popup-lamellen-value');
      const popupStatusJalousie = document.getElementById('dg-buero-popup-status-jalousie');
      const popupStatusLamellen = document.getElementById('dg-buero-popup-status-lamellen');
      const btnStop = document.getElementById('dg-buero-btn-stop');
      const btnPreset1 = document.getElementById('dg-buero-btn-preset-1');
      const btnPreset2 = document.getElementById('dg-buero-btn-preset-2');
      
      // Additional popup buttons
      const btnJalousieUp = document.getElementById('dg-buero-btn-jalousie-up');
      const btnJalousieDown = document.getElementById('dg-buero-btn-jalousie-down');
      const btnLamellenUp = document.getElementById('dg-buero-btn-lamellen-up');
      const btnLamellenDown = document.getElementById('dg-buero-btn-lamellen-down');
    
      // Funktion für das Senden von Werten an Datenpunkte
      function sendToDP(dp, value) {
        if (typeof vis !== 'undefined') {
          vis.setValue(dp, value);
        } else {
          console.log('Sending to ' + dp + ': ' + value);
        }
      }
    
      // Funktion für das Halten von Tasten
      function setupHoldButton(button, shortDP, longDP, value) {
        button.addEventListener('mousedown', function() {
          holdTimeout = setTimeout(function() {
            sendToDP(longDP, value);
            holdTimeout = null;
          }, 400);
        });
    
        button.addEventListener('mouseup', function() {
          if (holdTimeout) {
            clearTimeout(holdTimeout);
            sendToDP(shortDP, value);
          }
        });
    
        button.addEventListener('mouseleave', function() {
          if (holdTimeout) {
            clearTimeout(holdTimeout);
          }
        });
        
        // Touch-Unterstützung für mobile Geräte
        button.addEventListener('touchstart', function(e) {
          e.preventDefault();
          holdTimeout = setTimeout(function() {
            sendToDP(longDP, value);
            holdTimeout = null;
          }, 400);
        });
    
        button.addEventListener('touchend', function(e) {
          e.preventDefault();
          if (holdTimeout) {
            clearTimeout(holdTimeout);
            sendToDP(shortDP, value);
          }
        });
      }
    
      // Popup öffnen/schließen
      function openPopup() {
        jalousiePopup.classList.add('active');
        popupOverlay.style.display = 'block';
        
        // Slider immer auf 0 setzen
        sliderJalousie.value = 0;
        sliderLamellen.value = 0;
        
        // Value-Displays auf 0 setzen
        popupJalousieValue.textContent = 0;
        popupLamellenValue.textContent = 0;
        
        // Status-Anzeige aus Datenpunkten lesen
        if (typeof vis !== 'undefined') {
          let jalousieStatus = vis.states.attr(statusJalousieDP + '.val');
          let lamellenStatus = vis.states.attr(statusLamellenDP + '.val');
          
          if (jalousieStatus !== undefined && jalousieStatus !== null) {
            popupStatusJalousie.textContent = parseInt(jalousieStatus) + '%';
          }
          
          if (lamellenStatus !== undefined && lamellenStatus !== null) {
            popupStatusLamellen.textContent = parseInt(lamellenStatus) + '%';
          }
        }
      }
      
      function closePopupFunc() {
        jalousiePopup.classList.remove('active');
        popupOverlay.style.display = 'none';
      }
    
      // Add touch event handlers to prevent background scrolling on mobile
      document.getElementById('dg-buero-jalousie-popup-overlay').addEventListener('touchmove', function(e) {
        e.preventDefault();
      }, { passive: false });
      
      document.getElementById('dg-buero-jalousie-popup').addEventListener('touchmove', function(e) {
        // Only prevent if the touch is not on a slider
        if (!e.target.classList.contains('jalousie-slider-vertical')) {
          e.preventDefault();
        }
      }, { passive: false });
      
      // Ensure sliders can be used on mobile
      ['dg-buero-slider-jalousie', 'dg-buero-slider-lamellen'].forEach(id => {
        const slider = document.getElementById(id);
        slider.addEventListener('touchmove', function(e) {
          e.stopPropagation(); // Allow slider to function normally
        }, { passive: true });
      });
      
      // Event-Listener für Popup
      jalousieIcon.addEventListener('click', openPopup);
      closePopup.addEventListener('click', closePopupFunc);
      popupOverlay.addEventListener('click', closePopupFunc);
    
      // Slider-Events mit Echtzeit-Aktualisierung
      sliderJalousie.addEventListener('input', function() {
        const value = parseInt(this.value);
        popupJalousieValue.textContent = value;
      });
    
      sliderJalousie.addEventListener('change', function() {
        const value = parseInt(this.value);
        sendToDP(absoluteJalousieDP, value);
        // Don't update statusValue directly since it comes from the datapoint
      });
    
      sliderLamellen.addEventListener('input', function() {
        const value = parseInt(this.value);
        popupLamellenValue.textContent = value;
        // Also update the status bar immediately for better user feedback
        popupStatusLamellen.textContent = value + '%';
      });
    
      sliderLamellen.addEventListener('change', function() {
        const value = parseInt(this.value);
        sendToDP(absoluteLamellenDP, value);
        // The status will be updated via datapoint binding
      });
    
      // STOP-Button und Preset-Buttons
      btnStop.addEventListener('click', function() {
        sendToDP(lamellenDP, true);
      });
    
      btnPreset1.addEventListener('click', function() {
        sendToDP(absoluteJalousieDP, 50);
      });
    
      btnPreset2.addEventListener('click', function() {
        sendToDP(absoluteJalousieDP, 100);
      });
      
      // Setup popup arrow buttons with same logic as main widget
      setupHoldButton(btnJalousieUp, lamellenDP, jalousienDP, false);
      setupHoldButton(btnJalousieDown, lamellenDP, jalousienDP, true);
      setupHoldButton(btnLamellenUp, lamellenDP, lamellenDP, false);
      setupHoldButton(btnLamellenDown, lamellenDP, lamellenDP, true);
    
      // Button-Konfiguration
      setupHoldButton(btnUp, lamellenDP, jalousienDP, false);
      setupHoldButton(btnDown, lamellenDP, jalousienDP, true);
    
      // Initialisierung für ioBroker
      function init() {
        // Status-Wert überwachen
        if (typeof vis !== 'undefined') {
    	
         // Jalousie-Status überwachen
    vis.states.bind(statusJalousieDP + '.val', function(e, newVal, oldVal) {
        if (newVal !== undefined) {  // Prüfung hinzugefügt
            jalousieValue = parseInt(newVal);
            statusElement.textContent = jalousieValue;
            sliderJalousie.value = jalousieValue;
            if (popupJalousieValue) {
                popupJalousieValue.textContent = jalousieValue;
            }
            if (popupStatusJalousie) {
                popupStatusJalousie.textContent = jalousieValue + '%';
            }
        }
    });
    
    // Lamellen-Status überwachen
    vis.states.bind(statusLamellenDP + '.val', function(e, newVal, oldVal) {
        if (newVal !== undefined) {
            lamellenValue = parseInt(newVal);
            sliderLamellen.value = lamellenValue;
            if (popupLamellenValue) {
                popupLamellenValue.textContent = lamellenValue;
            }
            if (popupStatusLamellen) {
                popupStatusLamellen.textContent = lamellenValue + '%';
            }
        }
    });
    
    
          
          // Anfangswerte laden
          if (vis.states[statusJalousieDP + '.val'] !== undefined) {
            jalousieValue = parseInt(vis.states[statusJalousieDP + '.val']);
            statusElement.textContent = jalousieValue;
            sliderJalousie.value = jalousieValue;
            popupJalousieValue.textContent = jalousieValue;
            popupStatusJalousie.textContent = jalousieValue + '%';
          }
          
          if (vis.states[statusLamellenDP + '.val'] !== undefined) {
            lamellenValue = parseInt(vis.states[statusLamellenDP + '.val']);
            sliderLamellen.value = lamellenValue;
            popupLamellenValue.textContent = lamellenValue;
            popupStatusLamellen.textContent = lamellenValue + '%';
          }
        } else {
          // Fallback für Testumgebungen
          jalousieValue = 50;
          lamellenValue = 50;
          statusElement.textContent = jalousieValue;
          sliderJalousie.value = jalousieValue;
          sliderLamellen.value = lamellenValue;
          popupJalousieValue.textContent = jalousieValue;
          popupLamellenValue.textContent = lamellenValue;
          popupStatusJalousie.textContent = jalousieValue + '%';
          popupStatusLamellen.textContent = lamellenValue + '%';
        }
      }
    
      // Wenn vis bereits geladen ist
      if (typeof vis !== 'undefined') {
        init();
      } else {
        // Falls vis noch nicht geladen ist (sollte in ioBroker nicht passieren)
        document.addEventListener('iobrokerReady', init);
      }
    </script>
    
    OliverIOO 1 Reply Last reply
    0
    • S smartysmart

      Hey Leute,
      mir fehlen leider die Programmierkentnnise um das HTML Widget selber zu machen also habe ich mir mit Claude.ai etwas Hilfe geholt.

      Ich bin auch fast am Ziel meines Vorhabens allerdings bekomme ich einen Fehler seit Stunden nicht heraus.

      Ich habe mir ein Widget gemacht wo ich Jalousien direkt hoch und runter fahren kann. Und links davon der aktuelle Status von der Jalousie angezeigt werden soll
      ab1d8a68-cfc6-4ab4-a7d0-d280c0a7da5e-image.png
      Hier ist bereits der erste Fehler weil da 0% angezeigt wird statt der echte Wert.

      Wenn ich auf das Widget klicke wird ein PopUp geöffnet mit mehr Funktionen, auch hier werden die aktuellen Werte nicht angezeigt.
      237926cc-d26e-41d9-a35a-0c3f7266ee23-image.png

      Der Regler funktioniert und die Prozente werden beim bedienen angezeigt,
      5045dbd3-7d92-4d87-b56b-0a1c45356a0e-image.png allerdings nur solange das PopUp offen ist. sobal ich das schliese und neu öffne steht oben wieder 0

      P.S im Editor wird die Jalousien Position agezeigt
      529238eb-dc74-4794-8b44-0555e47a277e-image.png

      Ich gehe aktuell also davon aus, dass auch die Werte im PopUp im Editor angezeigt werden und das das Problem nur in der VIS Ansicht ist.

      Das ist der Code dazu

      <style>
        .jalousie-widget-container {
          width: 100%;
          height: 100%;
          box-sizing: border-box;
          border-radius: 20px;
          padding: 0;
          background: #f5f5f5;
          font-family: sans-serif;
          position: relative;
          margin: 0 auto;
          border: 1px solid #e0e0e0;
          display: flex;
          flex-direction: column;
        }
        
        .jalousie-widget-header {
          display: flex;
          align-items: center;
          padding: calc(min(10px, 3%));
          border-bottom: 1px dotted #ccc;
          flex: 0 0 auto;
        }
        
        .jalousie-widget-icon {
          width: calc(min(24px, 15%));
          height: calc(min(24px, 15%));
          margin-right: calc(min(10px, 3%));
          min-width: 16px;
          min-height: 16px;
        }
        
        .jalousie-widget-title {
          font-size: calc(min(14px, 4vw));
          margin: 0;
          padding: calc(min(8px, 3%)) 0;
          text-align: left;
          border-bottom: 1px dotted #ccc;
          padding-left: calc(min(10px, 3%));
          flex: 0 0 auto;
        }
        
        .jalousie-widget-status {
          display: flex;
          align-items: center;
          padding: calc(min(10px, 3%)) calc(min(12px, 4%));
          justify-content: space-between;
          flex: 1 0 auto;
        }
        
        .jalousie-status-text {
          font-size: calc(min(16px, 4.5vw));
          margin: 0;
          font-weight: 500;
        }
        
        .jalousie-widget-buttons {
          display: flex;
        }
      
        .jalousie-widget-button {
          width: calc(min(44px, 35%));
          height: calc(min(44px, 35%));
          display: flex;
          align-items: center;
          justify-content: center;
          border: 1px solid #ccc;
          background: white;
          border-radius: 50%;
          margin-left: 8px;
          cursor: pointer;
          font-size: calc(min(26px, 8vw));
          padding: 0;
          min-width: 40px;
          min-height: 40px;
          font-weight: bold;
          color: #333;
        }
      
        .jalousie-popup-overlay {
          position: fixed;
          top: 0;
          left: 0;
          width: 100%;
          height: 100%;
          background: rgba(0, 0, 0, 0.5);
          display: none;
          z-index: 9998;
          touch-action: none;
        }
      
        .jalousie-popup {
          display: none;
          position: fixed;
          top: 5%;
          left: 5%;
          width: 90%;
          height: 90%;
          max-height: 90vh;
          background: white;
          border-radius: 8px;
          box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
          z-index: 9999;
          font-family: sans-serif;
          overflow: hidden;
          touch-action: none;
        }
        
        .jalousie-popup.active {
          display: flex;
          flex-direction: column;
        }
        
        .jalousie-popup-header {
          padding: 15px;
          border-bottom: 1px solid #eee;
          font-size: 18px;
          font-weight: 500;
          display: flex;
          justify-content: space-between;
          align-items: center;
        }
        
        .jalousie-popup-status {
          padding: 15px;
          border-bottom: 1px solid #eee;
          display: flex;
          justify-content: space-between;
          align-items: center;
          background: #f9f9f9;
        }
        
        .jalousie-popup-status-item {
          display: flex;
          align-items: center;
        }
        
        .jalousie-popup-status-label {
          font-size: 14px;
          color: #666;
          margin-right: 10px;
        }
        
        .jalousie-popup-status-value {
          font-size: 16px;
          font-weight: bold;
        }
        
        .jalousie-popup-content {
          flex: 1;
          display: flex;
          padding: 20px;
          justify-content: center;
          overflow: hidden;
          touch-action: none;
        }
        
        .jalousie-sliders-container {
          display: flex;
          width: 100%;
          max-width: 600px;
          justify-content: space-around;
          align-items: flex-start;
        }
        
        .jalousie-slider-column {
          display: flex;
          flex-direction: column;
          align-items: center;
          width: 45%;
        }
        
        .jalousie-slider-wrapper {
          display: flex;
          flex-direction: column;
          align-items: center;
          width: 100%;
          height: 100%;
          max-height: 500px;
        }
        
        .jalousie-slider-icon {
          margin-bottom: 10px;
          width: 24px;
          height: 24px;
        }
        
        .jalousie-slider-vertical {
          -webkit-appearance: slider-vertical;
          appearance: slider-vertical;
          height: 80%;
          min-height: 200px;
          width: 50px;
          margin: 20px 0;
          transform: rotate(180deg);
          background: #e0e0e0;
          border-radius: 25px;
          outline: none;
          cursor: pointer;
        }
        
        .jalousie-slider-vertical::-webkit-slider-thumb {
          -webkit-appearance: none;
          appearance: none;
          width: 60px;
          height: 60px;
          background: #007bff;
          border-radius: 50%;
          cursor: pointer;
          box-shadow: 0 0 5px rgba(0,0,0,0.2);
        }
        
        .jalousie-slider-vertical::-moz-range-thumb {
          width: 60px;
          height: 60px;
          background: #007bff;
          border-radius: 50%;
          cursor: pointer;
          box-shadow: 0 0 5px rgba(0,0,0,0.2);
          border: none;
        }
        
        .jalousie-slider-label {
          font-size: 16px;
          margin-top: 10px;
          text-align: center;
        }
        
        .jalousie-slider-buttons {
          display: flex;
          gap: 10px;
          margin-top: 15px;
        }
        
        .jalousie-slider-button {
          width: 50px;
          height: 50px;
          display: flex;
          align-items: center;
          justify-content: center;
          border: 1px solid #ccc;
          background: white;
          border-radius: 50%;
          cursor: pointer;
          font-size: 24px;
          padding: 0;
          font-weight: bold;
          touch-action: manipulation;
          color: #333;
        }
        
        .jalousie-slider-button:hover {
          background: #f5f5f5;
        }
        
        .jalousie-slider-button:active {
          background: #e0e0e0;
        }
        
        .jalousie-popup-footer {
          padding: 15px;
          border-top: 1px solid #eee;
          display: flex;
          justify-content: space-between;
        }
        
        .jalousie-popup-button {
          padding: 10px 20px;
          background: #f5f5f5;
          border: 1px solid #ddd;
          border-radius: 4px;
          font-size: 14px;
          cursor: pointer;
          color: #333;
        }
        
        .jalousie-popup-button:hover {
          background: #eee;
        }
        
        .jalousie-value-display {
          font-size: 20px;
          font-weight: bold;
          margin-top: 10px;
          margin-bottom: 20px;
        }
      </style>
      
      <div class="jalousie-widget-container" id="dg-buero-jalousie-widget">
        <div class="jalousie-widget-header">
          <svg id="dg-buero-jalousie-icon" class="jalousie-widget-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="cursor: pointer;">
            <rect x="4" y="4" width="16" height="16" rx="2" />
            <line x1="4" y1="12" x2="20" y2="12" />
            <line x1="7" y1="8" x2="17" y2="8" />
            <line x1="7" y1="16" x2="17" y2="16" />
          </svg>
        </div>
        
        <div class="jalousie-widget-title">
          DG Büro
        </div>
        
        <div class="jalousie-widget-status">
          <div class="jalousie-status-text">
            <span id="dg-buero-jalousie-status">0</span>%
          </div>
          
          <div class="jalousie-widget-buttons">
            <button id="dg-buero-btn-up" class="jalousie-widget-button">↑</button>
            <button id="dg-buero-btn-down" class="jalousie-widget-button">↓</button>
          </div>
        </div>
      </div>
      
      <div id="dg-buero-jalousie-popup-overlay" class="jalousie-popup-overlay"></div>
      <div id="dg-buero-jalousie-popup" class="jalousie-popup">
        <div class="jalousie-popup-header">
          DG Büro
          <button id="dg-buero-close-popup" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #333;">×</button>
        </div>
        
        <div class="jalousie-popup-status">
          <div class="jalousie-popup-status-item">
            <span class="jalousie-popup-status-label">Jalousie:</span>
            <span class="jalousie-popup-status-value" id="dg-buero-popup-status-jalousie">0%</span>
          </div>
          <div class="jalousie-popup-status-item">
            <span class="jalousie-popup-status-label">Lamellen:</span>
            <span class="jalousie-popup-status-value" id="dg-buero-popup-status-lamellen">0%</span>
          </div>
        </div>
        
        <div class="jalousie-popup-content">
          <div class="jalousie-sliders-container">
            <div class="jalousie-slider-column">
              <div class="jalousie-slider-wrapper">
                <svg class="jalousie-slider-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <rect x="6" y="4" width="12" height="16" rx="1" />
                  <line x1="6" y1="8" x2="18" y2="8" />
                  <line x1="6" y1="12" x2="18" y2="12" />
                  <line x1="6" y1="16" x2="18" y2="16" />
                </svg>
                <div class="jalousie-value-display"><span id="dg-buero-popup-jalousie-value">0</span>%</div>
                <input type="range" min="0" max="100" value="0" id="dg-buero-slider-jalousie" class="jalousie-slider-vertical" orient="vertical" />
                <div class="jalousie-slider-label">Position Jalousie</div>
                <div class="jalousie-slider-buttons">
                  <button id="dg-buero-btn-jalousie-up" class="jalousie-slider-button">↑</button>
                  <button id="dg-buero-btn-jalousie-down" class="jalousie-slider-button">↓</button>
                </div>
              </div>
            </div>
            
            <div class="jalousie-slider-column">
              <div class="jalousie-slider-wrapper">
                <svg class="jalousie-slider-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <path d="M12 3 L4 10 L20 10 Z" />
                  <path d="M12 10 L4 17 L20 17 Z" />
                </svg>
                <div class="jalousie-value-display"><span id="dg-buero-popup-lamellen-value">0</span>%</div>
                <input type="range" min="0" max="100" value="0" id="dg-buero-slider-lamellen" class="jalousie-slider-vertical" orient="vertical" />
                <div class="jalousie-slider-label">Position Lamellen</div>
                <div class="jalousie-slider-buttons">
                  <button id="dg-buero-btn-lamellen-up" class="jalousie-slider-button">↑</button>
                  <button id="dg-buero-btn-lamellen-down" class="jalousie-slider-button">↓</button>
                </div>
              </div>
            </div>
          </div>
        </div>
        
        <div class="jalousie-popup-footer">
          <button id="dg-buero-btn-stop" class="jalousie-popup-button">STOP</button>
          <div>
            <button id="dg-buero-btn-preset-1" class="jalousie-popup-button">50%</button>
            <button id="dg-buero-btn-preset-2" class="jalousie-popup-button">100%</button>
          </div>
        </div>
      </div>
      
      <script>
        // Datenpunkte definieren - Diese müssen an dein ioBroker-System angepasst sein
        const statusDP = 'openknx.0.Status.Jalousinen_Lamellen.Position_Jalousine_DG_Büro_status';  // For main widget display
        const statusJalousieDP = 'openknx.0.Status.Jalousinen_Lamellen.Position_Jalousine_DG_Büro_status';  // Jalousie status in popup
        const statusLamellenDP = 'openknx.0.Status.Jalousinen_Lamellen.Position_Lamellen_DG_Büro_status';  // Lamella status in popup
        const lamellenDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Balkon_Lamellen';
        const jalousienDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Balkon_Jalousien';
        const absoluteJalousieDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Absolute_Position_Jalousine';
        const absoluteLamellenDP = 'openknx.0.Schalten.Jalousinen.DG_Büro_Absolute_Position_Lamellen';
      
        let holdTimeout;
        let statusValue = 0;
        let jalousieValue = 0;  // Status value for jalousie
        let lamellenValue = 0;  // Status value for lamella
      
        // DOM-Elemente
        const btnUp = document.getElementById('dg-buero-btn-up');
        const btnDown = document.getElementById('dg-buero-btn-down');
        const jalousieIcon = document.getElementById('dg-buero-jalousie-icon');
        const closePopup = document.getElementById('dg-buero-close-popup');
        const jalousiePopup = document.getElementById('dg-buero-jalousie-popup');
        const popupOverlay = document.getElementById('dg-buero-jalousie-popup-overlay');
        const statusElement = document.getElementById('dg-buero-jalousie-status');
        const sliderJalousie = document.getElementById('dg-buero-slider-jalousie');
        const sliderLamellen = document.getElementById('dg-buero-slider-lamellen');
        
        // DOM-Elemente für die Popup-Werte
        const popupJalousieValue = document.getElementById('dg-buero-popup-jalousie-value');
        const popupLamellenValue = document.getElementById('dg-buero-popup-lamellen-value');
        const popupStatusJalousie = document.getElementById('dg-buero-popup-status-jalousie');
        const popupStatusLamellen = document.getElementById('dg-buero-popup-status-lamellen');
        const btnStop = document.getElementById('dg-buero-btn-stop');
        const btnPreset1 = document.getElementById('dg-buero-btn-preset-1');
        const btnPreset2 = document.getElementById('dg-buero-btn-preset-2');
        
        // Additional popup buttons
        const btnJalousieUp = document.getElementById('dg-buero-btn-jalousie-up');
        const btnJalousieDown = document.getElementById('dg-buero-btn-jalousie-down');
        const btnLamellenUp = document.getElementById('dg-buero-btn-lamellen-up');
        const btnLamellenDown = document.getElementById('dg-buero-btn-lamellen-down');
      
        // Funktion für das Senden von Werten an Datenpunkte
        function sendToDP(dp, value) {
          if (typeof vis !== 'undefined') {
            vis.setValue(dp, value);
          } else {
            console.log('Sending to ' + dp + ': ' + value);
          }
        }
      
        // Funktion für das Halten von Tasten
        function setupHoldButton(button, shortDP, longDP, value) {
          button.addEventListener('mousedown', function() {
            holdTimeout = setTimeout(function() {
              sendToDP(longDP, value);
              holdTimeout = null;
            }, 400);
          });
      
          button.addEventListener('mouseup', function() {
            if (holdTimeout) {
              clearTimeout(holdTimeout);
              sendToDP(shortDP, value);
            }
          });
      
          button.addEventListener('mouseleave', function() {
            if (holdTimeout) {
              clearTimeout(holdTimeout);
            }
          });
          
          // Touch-Unterstützung für mobile Geräte
          button.addEventListener('touchstart', function(e) {
            e.preventDefault();
            holdTimeout = setTimeout(function() {
              sendToDP(longDP, value);
              holdTimeout = null;
            }, 400);
          });
      
          button.addEventListener('touchend', function(e) {
            e.preventDefault();
            if (holdTimeout) {
              clearTimeout(holdTimeout);
              sendToDP(shortDP, value);
            }
          });
        }
      
        // Popup öffnen/schließen
        function openPopup() {
          jalousiePopup.classList.add('active');
          popupOverlay.style.display = 'block';
          
          // Slider immer auf 0 setzen
          sliderJalousie.value = 0;
          sliderLamellen.value = 0;
          
          // Value-Displays auf 0 setzen
          popupJalousieValue.textContent = 0;
          popupLamellenValue.textContent = 0;
          
          // Status-Anzeige aus Datenpunkten lesen
          if (typeof vis !== 'undefined') {
            let jalousieStatus = vis.states.attr(statusJalousieDP + '.val');
            let lamellenStatus = vis.states.attr(statusLamellenDP + '.val');
            
            if (jalousieStatus !== undefined && jalousieStatus !== null) {
              popupStatusJalousie.textContent = parseInt(jalousieStatus) + '%';
            }
            
            if (lamellenStatus !== undefined && lamellenStatus !== null) {
              popupStatusLamellen.textContent = parseInt(lamellenStatus) + '%';
            }
          }
        }
        
        function closePopupFunc() {
          jalousiePopup.classList.remove('active');
          popupOverlay.style.display = 'none';
        }
      
        // Add touch event handlers to prevent background scrolling on mobile
        document.getElementById('dg-buero-jalousie-popup-overlay').addEventListener('touchmove', function(e) {
          e.preventDefault();
        }, { passive: false });
        
        document.getElementById('dg-buero-jalousie-popup').addEventListener('touchmove', function(e) {
          // Only prevent if the touch is not on a slider
          if (!e.target.classList.contains('jalousie-slider-vertical')) {
            e.preventDefault();
          }
        }, { passive: false });
        
        // Ensure sliders can be used on mobile
        ['dg-buero-slider-jalousie', 'dg-buero-slider-lamellen'].forEach(id => {
          const slider = document.getElementById(id);
          slider.addEventListener('touchmove', function(e) {
            e.stopPropagation(); // Allow slider to function normally
          }, { passive: true });
        });
        
        // Event-Listener für Popup
        jalousieIcon.addEventListener('click', openPopup);
        closePopup.addEventListener('click', closePopupFunc);
        popupOverlay.addEventListener('click', closePopupFunc);
      
        // Slider-Events mit Echtzeit-Aktualisierung
        sliderJalousie.addEventListener('input', function() {
          const value = parseInt(this.value);
          popupJalousieValue.textContent = value;
        });
      
        sliderJalousie.addEventListener('change', function() {
          const value = parseInt(this.value);
          sendToDP(absoluteJalousieDP, value);
          // Don't update statusValue directly since it comes from the datapoint
        });
      
        sliderLamellen.addEventListener('input', function() {
          const value = parseInt(this.value);
          popupLamellenValue.textContent = value;
          // Also update the status bar immediately for better user feedback
          popupStatusLamellen.textContent = value + '%';
        });
      
        sliderLamellen.addEventListener('change', function() {
          const value = parseInt(this.value);
          sendToDP(absoluteLamellenDP, value);
          // The status will be updated via datapoint binding
        });
      
        // STOP-Button und Preset-Buttons
        btnStop.addEventListener('click', function() {
          sendToDP(lamellenDP, true);
        });
      
        btnPreset1.addEventListener('click', function() {
          sendToDP(absoluteJalousieDP, 50);
        });
      
        btnPreset2.addEventListener('click', function() {
          sendToDP(absoluteJalousieDP, 100);
        });
        
        // Setup popup arrow buttons with same logic as main widget
        setupHoldButton(btnJalousieUp, lamellenDP, jalousienDP, false);
        setupHoldButton(btnJalousieDown, lamellenDP, jalousienDP, true);
        setupHoldButton(btnLamellenUp, lamellenDP, lamellenDP, false);
        setupHoldButton(btnLamellenDown, lamellenDP, lamellenDP, true);
      
        // Button-Konfiguration
        setupHoldButton(btnUp, lamellenDP, jalousienDP, false);
        setupHoldButton(btnDown, lamellenDP, jalousienDP, true);
      
        // Initialisierung für ioBroker
        function init() {
          // Status-Wert überwachen
          if (typeof vis !== 'undefined') {
      	
           // Jalousie-Status überwachen
      vis.states.bind(statusJalousieDP + '.val', function(e, newVal, oldVal) {
          if (newVal !== undefined) {  // Prüfung hinzugefügt
              jalousieValue = parseInt(newVal);
              statusElement.textContent = jalousieValue;
              sliderJalousie.value = jalousieValue;
              if (popupJalousieValue) {
                  popupJalousieValue.textContent = jalousieValue;
              }
              if (popupStatusJalousie) {
                  popupStatusJalousie.textContent = jalousieValue + '%';
              }
          }
      });
      
      // Lamellen-Status überwachen
      vis.states.bind(statusLamellenDP + '.val', function(e, newVal, oldVal) {
          if (newVal !== undefined) {
              lamellenValue = parseInt(newVal);
              sliderLamellen.value = lamellenValue;
              if (popupLamellenValue) {
                  popupLamellenValue.textContent = lamellenValue;
              }
              if (popupStatusLamellen) {
                  popupStatusLamellen.textContent = lamellenValue + '%';
              }
          }
      });
      
      
            
            // Anfangswerte laden
            if (vis.states[statusJalousieDP + '.val'] !== undefined) {
              jalousieValue = parseInt(vis.states[statusJalousieDP + '.val']);
              statusElement.textContent = jalousieValue;
              sliderJalousie.value = jalousieValue;
              popupJalousieValue.textContent = jalousieValue;
              popupStatusJalousie.textContent = jalousieValue + '%';
            }
            
            if (vis.states[statusLamellenDP + '.val'] !== undefined) {
              lamellenValue = parseInt(vis.states[statusLamellenDP + '.val']);
              sliderLamellen.value = lamellenValue;
              popupLamellenValue.textContent = lamellenValue;
              popupStatusLamellen.textContent = lamellenValue + '%';
            }
          } else {
            // Fallback für Testumgebungen
            jalousieValue = 50;
            lamellenValue = 50;
            statusElement.textContent = jalousieValue;
            sliderJalousie.value = jalousieValue;
            sliderLamellen.value = lamellenValue;
            popupJalousieValue.textContent = jalousieValue;
            popupLamellenValue.textContent = lamellenValue;
            popupStatusJalousie.textContent = jalousieValue + '%';
            popupStatusLamellen.textContent = lamellenValue + '%';
          }
        }
      
        // Wenn vis bereits geladen ist
        if (typeof vis !== 'undefined') {
          init();
        } else {
          // Falls vis noch nicht geladen ist (sollte in ioBroker nicht passieren)
          document.addEventListener('iobrokerReady', init);
        }
      </script>
      
      OliverIOO Offline
      OliverIOO Offline
      OliverIO
      wrote on last edited by OliverIO
      #2

      @smartysmart

      Das Hauptproblem ist, das die ganzen datenpunkte nicht abonniert wurde.
      Vis sammelt beim Start (also bspw neuladen mit f5) zunächst erst einmal alle vorhandenen datenpunkte ein und abonniert diese beim Server.
      Erst dann sind die in der vis.states Auflistung vorhanden.
      Das passiert bei dir nicht. Daher auch keine Daten

      Du kannst mal hier nach meiner Funktion bindStates schauen.
      Die macht das mit der Angabe des datenpunkt namens.
      Du musst aber ein html Element mitgeben. Dort werden dann die trigger für die Änderung gespeichert, das die auch wieder entfernt werden. Ansonsten holst du dir früher oder später ein Speicherwerk, da sich mit der Zeit immer mehr trigger anhäufen und bei einer Änderung deine Funktionen dann zigmal aufgerufen werden.

      https://github.com/oweitman/ioBroker.tvprogram/blob/404b70e1e1886b545d9ae9f0b2dd7f2f27452179/widgets/tvprogram/js/tvprogram.js#L122

      Die variablen mit $ sind jquery Objekte.

      Meine Adapter und Widgets
      TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
      Links im Profil

      1 Reply Last reply
      0
      Reply
      • Reply as topic
      Log in to reply
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes


      Support us

      ioBroker
      Community Adapters
      Donate

      400

      Online

      32.4k

      Users

      81.4k

      Topics

      1.3m

      Posts
      Community
      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
      ioBroker Community 2014-2025
      logo
      • Login

      • Don't have an account? Register

      • Login or register to search.
      • First post
        Last post
      0
      • Recent
      • Tags
      • Unread 0
      • Categories
      • Unreplied
      • Popular
      • GitHub
      • Docu
      • Hilfe