Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • 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

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. [Skript] Wetter.com Forecast/Vorhersage

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    16
    1
    990

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    694

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.0k

[Skript] Wetter.com Forecast/Vorhersage

Geplant Angeheftet Gesperrt Verschoben JavaScript
17 Beiträge 5 Kommentatoren 292 Aufrufe 10 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • S Offline
    S Offline
    Schimi
    schrieb am zuletzt editiert von Schimi
    #1

    Hallo zusammen,

    da wetter.com die alten API-Versionen abschaltet, habe ich (in Zusammenarbeit mit Gemini) ein Skript für die API v4.0 (Meteonomiqs) erstellt. (und weil es keinen Adapter dafür gibt)

    Das Skript ist darauf ausgelegt, "Plug & Play" zu funktionieren, die Daten sauber in 0_userdata.0 zu strukturieren und dabei die Limits des Free-Accounts (100 Abrufe/Monat) im Blick zu behalten.

    Features
    API v4.0 Support: Nutzt die aktuellen Endpunkte von Meteonomiqs/wetter.com.

    Automatische Struktur: Legt alle benötigten Ordner und Datenpunkte unter 0_userdata.0.wetter_com selbständig an.

    Standort-Automatik: Liest Breiten- und Längengrad direkt aus den ioBroker-Systemeinstellungen.

    • Fallback/Override: Manuelle Koordinaten können im Skript hinterlegt werden (z.B. für Ferienhäuser).

    Limit-Wächter: Überwacht die Anzahl der API-Abrufe (täglich und monatlich).

    • Bei Fehler 429 (Limit erreicht) wird eine Warnung ausgegeben und der Abruf gestoppt, statt das Log vollzuschreiben.

    Server-Schutz (Random Cron): Um die API nicht zu überlasten, wenn viele User das Skript nutzen, werden die Abrufzeiten bei jedem Skriptstart zufällig innerhalb sinnvoller Zeitfenster (morgens 00:02–05:00 Uhr und nachmittags 13:02–17:00 Uhr) generiert.

    Cleanup: Wenn man die Anzahl der Vorhersage-Tage reduziert, werden überflüssige Datenpunkte automatisch gelöscht.

    Datenpunkte:

    • Datum

    • Min/Max Temperatur

    • Wetterzustand (Text & Code für Icons)

    • Regenwahrscheinlichkeit & Menge

    • Sonnenstunden

    • Wind (Geschwindigkeit & Richtung)

    Voraussetzungen
    API Key: Ihr benötigt einen kostenlosen API-Key von Meteonomiqs/wetter.com. (Free-Paket wählen).

    • https://www.meteonomiqs.com/de/wetter-api/#heading_PricePackages/

    Koordinaten: Sollten in den ioBroker Haupteinstellungen (System -> Einstellungen) hinterlegt sein.

    • (Kann im Skript, im Bereich "STANDORT KONFIGURATION", angepasst/geändert werden)

    Installation & Konfiguration

    1. Neues JS-Skript im ioBroker anlegen.

    2. Code hineinkopieren.

    3. Im Bereich --- KONFIGURATION --- euren API_KEY eintragen.

    4. Optional: FORECAST_DAYS anpassen (Standard: 7 Tage).

    info-Datenpunkt

    • Unter 0_userdata.0.wetter_com.info findet ihr Datenpunkte wie requests_month oder next_schedules, die ihr in der VIS anzeigen könnt, um euren Verbrauch zu überwachen.
    • Icon-URL wird als Datenpunkt ausgegeben
    • NEU (seit Version 1.4.10): Wochentage werden ausgegeben. Anpassung an eingestellte Sprache im ioBroker (mit fallback im Skript).
      der "weather_text" wird auch nach Systemsprache angepasst (z.B. "Leicht bewölkt" vs. "Cloudy").

    /**
    * ioBroker Script: Wetter.com Forecast API v4.0
    * API: https://doc.meteonomiqs.com/doc/forecast_v4_0.html
    * * Changelog:
    * 1.4.11: FIX: Fallback-Werte für Konfigurations-Variablen (ICON_BASE_URL, DEFAULT_LANGUAGE) integriert. 
    * Verhindert Abstürze ("ReferenceError"), wenn beim Update der Konfig-Bereich nicht aktualisiert wurde.
    * 1.4.10: Feature: Dynamische Spracheinstellung.
    * 1.4.8: Icon-URL Anpassung.
    * * free API-Key anfordern: https://www.meteonomiqs.com/de/wetter-api/#heading_PricePackages/
    */
    
    // --- KONFIGURATION ---
    const API_KEY = 'DEIN_API_KEY_HIER'; // <-- BITTE HIER DEINEN API-KEY EINTRAGEN
    const BASE_URL = 'https://forecast.meteonomiqs.com/v4_0';
    const ICON_BASE_URL = 'https://cs3.wettercomassets.com/wcomv5/images/icons/weather'; 
    const DP_PATH = '0_userdata.0.wetter_com';
    const DEFAULT_LANGUAGE = 'de'; // Fallback, falls System-Sprache nicht ermittelbar
    const FORECAST_DAYS = 7; 
    
    // --- STANDORT KONFIGURATION ---
    const MANUAL_LATITUDE = ''; 
    const MANUAL_LONGITUDE = '';
    const FORCE_MANUAL_LOCATION = false; 
    
    // --- RANDOMISIERUNG DER ZEITEN ---
    
    function getRandomCron(startHour, endHour, minMinute = 0) {
       const hour = Math.floor(Math.random() * (endHour - startHour + 1)) + startHour;
       let minute;
       if (hour === startHour) {
           minute = Math.floor(Math.random() * (60 - minMinute)) + minMinute;
       } else if (hour === endHour) {
           minute = 0; 
       } else {
           minute = Math.floor(Math.random() * 60);
       }
       return `${minute} ${hour} * * *`;
    }
    
    const cron1 = getRandomCron(0, 5, 2);
    const cron2 = getRandomCron(13, 17, 2);
    
    console.log(`[Wetter.com] Schedules für heute gesetzt auf: "${cron1}" und "${cron2}"`);
    
    // --- HILFSFUNKTIONEN ---
    
    /**
    * Formatiert ein Datum manuell in das Format: "DD.MM.YYYY"
    */
    function formatDate(dateStr) {
       if (!dateStr) return '';
       const date = new Date(dateStr);
       if (isNaN(date.getTime())) return dateStr;
       const day = String(date.getDate()).padStart(2, '0');
       const month = String(date.getMonth() + 1).padStart(2, '0');
       const year = date.getFullYear();
       return `${day}.${month}.${year}`;
    }
    
    /**
    * Ermittelt den Wochentag basierend auf der übergebenen Sprache (Locale)
    */
    function getDayName(dateStr, locale) {
       if (!dateStr) return '';
       const date = new Date(dateStr);
       if (isNaN(date.getTime())) return '';
       return date.toLocaleDateString(locale, { weekday: 'long' });
    }
    
    /**
    * Ermittelt Einstellungen (Coords + Sprache) aus ioBroker System-Config
    */
    async function getSystemSettings() {
       // Safety check für Variablen, falls User Config nicht aktualisiert hat
       const manualLat = (typeof MANUAL_LATITUDE !== 'undefined') ? MANUAL_LATITUDE : '';
       const manualLon = (typeof MANUAL_LONGITUDE !== 'undefined') ? MANUAL_LONGITUDE : '';
       const forceManual = (typeof FORCE_MANUAL_LOCATION !== 'undefined') ? FORCE_MANUAL_LOCATION : false;
       const defLang = (typeof DEFAULT_LANGUAGE !== 'undefined') ? DEFAULT_LANGUAGE : 'de';
    
       // 1. Manuelle Location?
       let coords = null;
       if (forceManual && manualLat && manualLon) {
            console.log(`[Wetter.com] Nutze manuell konfigurierte Koordinaten (Override).`);
            coords = { lat: parseFloat(manualLat).toFixed(3), lon: parseFloat(manualLon).toFixed(3) };
       }
    
       // 2. System Config lesen
       const systemConf = await new Promise((resolve) => {
           getObject('system.config', (err, obj) => {
               if (!err && obj && obj.common) {
                   resolve({
                       lat: obj.common.latitude ? parseFloat(obj.common.latitude).toFixed(3) : null,
                       lon: obj.common.longitude ? parseFloat(obj.common.longitude).toFixed(3) : null,
                       lang: obj.common.language || defLang
                   });
               } else {
                   resolve({ lat: null, lon: null, lang: defLang });
               }
           });
       });
    
       // 3. Fallback Logik
       if (!coords) {
           if (systemConf.lat && systemConf.lon) {
               coords = { lat: systemConf.lat, lon: systemConf.lon };
           } else if (manualLat && manualLon) {
               console.log('[Wetter.com] Warnung: Keine System-Koordinaten. Nutze Fallback.');
               coords = { lat: parseFloat(manualLat).toFixed(3), lon: parseFloat(manualLon).toFixed(3) };
           }
       }
    
       if (!coords) return null;
    
       return { ...coords, lang: systemConf.lang };
    }
    
    async function ensureStructure(path, index, isInfo = false) {
       if (isInfo) {
           await createStateAsync(`${DP_PATH}.info.last_sync`, '', false, { name: 'Letztes erfolgreiches Update', type: 'string', role: 'text' });
           await createStateAsync(`${DP_PATH}.info.requests_today`, 0, false, { name: 'Anfragen heute', type: 'number', role: 'value' });
           await createStateAsync(`${DP_PATH}.info.requests_month`, 0, false, { name: 'Anfragen aktueller Monat', type: 'number', role: 'value' });
           await createStateAsync(`${DP_PATH}.info.next_schedules`, '', false, { name: 'Geplante Abrufe', type: 'string', role: 'text' });
           return;
       }
    
       const states = {
           'date': { name: 'Datum', type: 'string', role: 'text', def: '' },
           'day_name': { name: 'Wochentag', type: 'string', role: 'text', def: '' },
           'temp_max': { name: 'Max Temperatur', type: 'number', unit: '°C', role: 'value.temperature.max', def: 0 },
           'temp_min': { name: 'Min Temperatur', type: 'number', unit: '°C', role: 'value.temperature.min', def: 0 },
           'weather_text': { name: 'Wetterzustand', type: 'string', role: 'weather.state', def: '' },
           'weather_code': { name: 'Wetter Code', type: 'number', role: 'value', def: 0 },
           'weather_icon': { name: 'Wetter Icon URL', type: 'string', role: 'weather.icon', def: '' },
           'prec_probability': { name: 'Regenrisiko', type: 'number', unit: '%', role: 'value.precipitation.probability', def: 0 },
           'prec_sum': { name: 'Regenmenge', type: 'number', unit: 'mm', role: 'value.precipitation', def: 0 },
           'sun_hours': { name: 'Sonnenstunden', type: 'number', unit: 'h', role: 'value.sunshine', def: 0 },
           'wind_speed_max': { name: 'Windböen Max', type: 'number', unit: 'km/h', role: 'value.speed.wind.gust', def: 0 },
           'wind_direction': { name: 'Windrichtung', type: 'string', role: 'weather.direction', def: '' }
       };
    
       for (const [id, config] of Object.entries(states)) {
           await createStateAsync(`${path}.${id}`, config.def, false, {
               name: `Tag ${index}: ${config.name}`,
               type: config.type,
               role: config.role,
               unit: config.unit || '',
               read: true,
               write: false
           });
       }
    }
    
    async function performRequest(url, options) {
       return new Promise((resolve, reject) => {
           httpGet(url, options, (error, response) => {
               if (error) return reject(new Error(error));
               if (response.statusCode === 429) return reject(new Error('LIMIT_REACHED'));
               if (response.statusCode !== 200) return reject(new Error(`HTTP Status ${response.statusCode}`));
               resolve(response);
           });
       });
    }
    
    async function cleanupObsoleteDays() {
       const channels = $(`${DP_PATH}.day_*`);
       channels.each(function(id) {
           const parts = id.split('.');
           const lastPart = parts[parts.length - 1]; 
           const dayIndex = parseInt(lastPart.replace('day_', ''));
           if (!isNaN(dayIndex) && dayIndex >= FORECAST_DAYS) {
               deleteObject(id, true);
           }
       });
    }
    
    async function updateUsageInfo() {
       const now = new Date();
       const day = String(now.getDate()).padStart(2, '0');
       const month = String(now.getMonth() + 1).padStart(2, '0');
       const year = now.getFullYear();
       const hours = String(now.getHours()).padStart(2, '0');
       const minutes = String(now.getMinutes()).padStart(2, '0');
       const seconds = String(now.getSeconds()).padStart(2, '0');
       
       const timestamp = `${day}.${month}.${year} ${hours}:${minutes}:${seconds}`;
       await setStateAsync(`${DP_PATH}.info.last_sync`, String(timestamp), true);
       
       const currentCountState = await getStateAsync(`${DP_PATH}.info.requests_today`);
       const currentCount = currentCountState ? (currentCountState.val || 0) : 0;
       await setStateAsync(`${DP_PATH}.info.requests_today`, currentCount + 1, true);
    
       const currentMonthState = await getStateAsync(`${DP_PATH}.info.requests_month`);
       const currentMonthCount = currentMonthState ? (currentMonthState.val || 0) : 0;
       await setStateAsync(`${DP_PATH}.info.requests_month`, currentMonthCount + 1, true);
       
       const s1 = cron1.split(' ');
       const s2 = cron2.split(' ');
       await setStateAsync(`${DP_PATH}.info.next_schedules`, `${s1[1].padStart(2,'0')}:${s1[0].padStart(2,'0')} Uhr & ${s2[1].padStart(2,'0')}:${s2[0].padStart(2,'0')} Uhr`, true);
    }
    
    // --- LOGIK ---
    
    async function fetchWeatherData() {
       try {
           const settings = await getSystemSettings();
           if (!settings) {
               console.error('[Wetter.com] Fehler: Keine Koordinaten gefunden!');
               return;
           }
           
           console.log(`[Wetter.com] Starte Abruf für Lat ${settings.lat}, Lon ${settings.lon} (Sprache: ${settings.lang})`);
    
           const url = `${BASE_URL}/forecast/${settings.lat}/${settings.lon}/summary`;
           const options = { headers: { 'x-api-key': API_KEY, 'Accept-Language': settings.lang } };
    
           try {
               const response = await performRequest(url, options);
               const data = JSON.parse(response.data);
               if (data && data.items) {
                   await processForecastData(data.items, settings.lang);
                   await cleanupObsoleteDays();
                   await updateUsageInfo();
               }
           } catch (err) {
               if (err.message === 'LIMIT_REACHED') {
                   console.error('[Wetter.com] Fehler 429: Monatslimit erreicht.');
               } else {
                   console.error(`[Wetter.com] Fehler: ${err.message}`);
               }
           }
       } catch (e) {
           console.error(`[Wetter.com] Script-Fehler: ${e.message}`);
       }
    }
    
    async function processForecastData(items, lang) {
       await ensureStructure('', 0, true); 
       const daysToProcess = Math.min(items.length, FORECAST_DAYS);
    
       // Fallback URL, falls Variable durch Teil-Update fehlt
       const iconBaseUrl = (typeof ICON_BASE_URL !== 'undefined') 
           ? ICON_BASE_URL 
           : 'https://cs3.wettercomassets.com/wcomv5/images/icons/weather';
    
       for (let i = 0; i < daysToProcess; i++) {
           const day = items[i];
           const dayPath = `${DP_PATH}.day_${i}`;
           await ensureStructure(dayPath, i);
    
           const germanDate = formatDate(day.date);
           const dayName = getDayName(day.date, lang);
           
           const weatherCode = day.weather?.state ?? 0;
           const iconUrl = `${iconBaseUrl}/d_${weatherCode}.svg`;
    
           await setStateAsync(`${dayPath}.date`, germanDate, true);
           await setStateAsync(`${dayPath}.day_name`, dayName, true);
           await setStateAsync(`${dayPath}.temp_max`, day.temperature?.max ?? 0, true);
           await setStateAsync(`${dayPath}.temp_min`, day.temperature?.min ?? 0, true);
           await setStateAsync(`${dayPath}.weather_text`, day.weather?.text || '', true);
           await setStateAsync(`${dayPath}.weather_code`, weatherCode, true);
           await setStateAsync(`${dayPath}.weather_icon`, iconUrl, true);
           await setStateAsync(`${dayPath}.prec_probability`, day.prec?.probability ?? 0, true);
           await setStateAsync(`${dayPath}.prec_sum`, day.prec?.sum ?? 0, true);
           await setStateAsync(`${dayPath}.sun_hours`, day.sunHours ?? 0, true);
           await setStateAsync(`${dayPath}.wind_speed_max`, day.wind?.max ?? 0, true);
           await setStateAsync(`${dayPath}.wind_direction`, day.wind?.direction || '', true);
       }
       console.log(`[Wetter.com] Update von ${daysToProcess} Tagen abgeschlossen.`);
    }
    
    schedule("0 0 * * *", () => {
       setState(`${DP_PATH}.info.requests_today`, 0, true);
    });
    
    schedule("0 0 1 * *", () => {
       setState(`${DP_PATH}.info.requests_month`, 0, true);
    });
    
    schedule(cron1, fetchWeatherData);
    schedule(cron2, fetchWeatherData);
    
    ensureStructure('', 0, true).then(() => {
       fetchWeatherData();
    });
    

    1 Antwort Letzte Antwort
    4
    • Pedder007P Offline
      Pedder007P Offline
      Pedder007
      schrieb am zuletzt editiert von
      #2

      Hi @schimi, evtl. bin ich ja gerade blind, aber wo finde ich Dein Script? :-)

      Pedder
      All @Proxmox/Bookworm auf HP Elitedesk 800 G4; Zigbee: ZigStar (LAN), ~110Devices
      Unifi, Motioneye/3Reolinks, PiHole, Bosch CS7800i via BBQKees/EMS-ESP, Fronius/BYD 11kWp via Modbus
      Under construction: Smart-WoMo auf Raspi4

      sigi234S S 2 Antworten Letzte Antwort
      0
      • Pedder007P Pedder007

        Hi @schimi, evtl. bin ich ja gerade blind, aber wo finde ich Dein Script? :-)

        sigi234S Offline
        sigi234S Offline
        sigi234
        Forum Testing Most Active
        schrieb am zuletzt editiert von
        #3

        @Pedder007 sagte in [Skript] Wetter.com Forecast/Vorhersage:

        Hi @schimi, evtl. bin ich ja gerade blind, aber wo finde ich Dein Script? :-)

        Klick mal auf Spoiler

        Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.
        Immer Daten sichern!

        1 Antwort Letzte Antwort
        1
        • Pedder007P Pedder007

          Hi @schimi, evtl. bin ich ja gerade blind, aber wo finde ich Dein Script? :-)

          S Offline
          S Offline
          Schimi
          schrieb am zuletzt editiert von
          #4

          @Pedder007 wie @sigi234 schon schrieb.... :-)

          0e527a28-1eb5-456d-bcd0-897f1a089dbe-image.png

          1 Antwort Letzte Antwort
          1
          • nik82N Offline
            nik82N Offline
            nik82
            Most Active
            schrieb am zuletzt editiert von
            #5

            @schimi
            Ich finde dein Script absolut perfekt! Vielen lieben Dank, wollte schon lange die Werte von wetter.com mal haben.
            Da mir das so gut gefällt würde ich gerne komplett umsteigen.

            Hat irgendwer eine Lösung oder vielleicht ein fertiges Widget für VIS, wo man Symbole anzeigen kann?

            Man müsste ja den "weather_text" in ein Symbol umwandlen und dann mit "String img src" oder "Image 8" Widget arbeiten, aber dazu müsste man erstmal alle Zustände von "weather_text" haben. Aber vielleicht hat sich ja einer schon die Mühe gemacht?

            1 Antwort Letzte Antwort
            1
            • S Offline
              S Offline
              Schimi
              schrieb am zuletzt editiert von
              #6

              die stehen in der API...

              ich muss jetzt erstmal (ein Monat) warten (habe da kein druck weil die grundfunktion geht) weil ich die 100 abfragen erreicht habe... wollte dann aber mal schauen, die Std vorhhersagen einzubauen.

              kann dann auch nach einer brauchbareren lösung für die Icons gucken...

              habs gemacht weio wetter.com bei mir die besten vorhersagen liefert, openweathermap und DasWetter waren meistens (extrem) daneben....

              Ich bleibe aber dran 😉

              1 Antwort Letzte Antwort
              1
              • nik82N Offline
                nik82N Offline
                nik82
                Most Active
                schrieb am zuletzt editiert von
                #7

                Wi finde ich die Übersicht der einzelnen "weather_text" Passagen?

                Hab hier was gefunden:
                https://doc.meteonomiqs.com/doc/forecast_v4_0.html#section/References/Moonphase-and-Moonzodiac

                Aber das ist englisch und der Text im Iobroker ist ja Deutsch.
                Jetzt bräuchte ich die korrekte übersetzung, hätte da nämlich schon eine Idee wegen den Symbolen.
                Einfach die Symbole nach dem Text bennnen und dann mit Bindings arbeiten.
                Würde da mal was basteln wenn ich das komplett hätte.

                Und ja, ich finde wetter.com auch die beste Vorhersage 😀

                1 Antwort Letzte Antwort
                0
                • Pedder007P Offline
                  Pedder007P Offline
                  Pedder007
                  schrieb am zuletzt editiert von Pedder007
                  #8

                  @schimi und @sigi234 danke, super!
                  Ich war echt blind :-) ... und hat auf Anhieb funktioniert :-)

                  Pedder
                  All @Proxmox/Bookworm auf HP Elitedesk 800 G4; Zigbee: ZigStar (LAN), ~110Devices
                  Unifi, Motioneye/3Reolinks, PiHole, Bosch CS7800i via BBQKees/EMS-ESP, Fronius/BYD 11kWp via Modbus
                  Under construction: Smart-WoMo auf Raspi4

                  1 Antwort Letzte Antwort
                  0
                  • S Offline
                    S Offline
                    Schimi
                    schrieb am zuletzt editiert von
                    #9

                    @nik82

                    Habe eine neue Version (1.4.8) unter dem Spoiler ganz oben versteckt.
                    Diese gibt auch die Icon-URL im Datenpunkt aus ;-)

                    nik82N 1 Antwort Letzte Antwort
                    2
                    • S Schimi

                      @nik82

                      Habe eine neue Version (1.4.8) unter dem Spoiler ganz oben versteckt.
                      Diese gibt auch die Icon-URL im Datenpunkt aus ;-)

                      nik82N Offline
                      nik82N Offline
                      nik82
                      Most Active
                      schrieb am zuletzt editiert von
                      #10

                      @Schimi sagte in [Skript] Wetter.com Forecast/Vorhersage:

                      @nik82

                      Habe eine neue Version (1.4.8) unter dem Spoiler ganz oben versteckt.
                      Diese gibt auch die Icon-URL im Datenpunkt aus ;-)

                      Hammer! Vielen lieben Dank 😀
                      Probiere ich morgen gleich aus.

                      1 Antwort Letzte Antwort
                      0
                      • nik82N Offline
                        nik82N Offline
                        nik82
                        Most Active
                        schrieb zuletzt editiert von
                        #11

                        @schimi
                        Vielen Dank für die Icons, funktionieren wunderbar.
                        Jetzt hätte ich noch eines: Kannst du irgendwie den Wochentag noch einblenden lassen, ich bekomme es einfach nicht hin, dass Datum in einen Wochentag im VIS zu konvertieren und auf Aliase oder ein extra Javascript hab ich eigentlich keine Lust :-)

                        S 1 Antwort Letzte Antwort
                        0
                        • nik82N nik82

                          @schimi
                          Vielen Dank für die Icons, funktionieren wunderbar.
                          Jetzt hätte ich noch eines: Kannst du irgendwie den Wochentag noch einblenden lassen, ich bekomme es einfach nicht hin, dass Datum in einen Wochentag im VIS zu konvertieren und auf Aliase oder ein extra Javascript hab ich eigentlich keine Lust :-)

                          S Offline
                          S Offline
                          Schimi
                          schrieb zuletzt editiert von
                          #12

                          @nik82 siehe Spoiler ;-)

                          1 Antwort Letzte Antwort
                          1
                          • nik82N Offline
                            nik82N Offline
                            nik82
                            Most Active
                            schrieb zuletzt editiert von
                            #13

                            Du bist echt der Beste!!!!
                            Vielen Dank :-)

                            1 Antwort Letzte Antwort
                            0
                            • S Offline
                              S Offline
                              Schimi
                              schrieb zuletzt editiert von
                              #14

                              Danke, aber ich mache nicht viel...

                              Das meiste macht "Gemini"

                              1 Antwort Letzte Antwort
                              0
                              • M Offline
                                M Offline
                                MartyBr
                                schrieb zuletzt editiert von
                                #15

                                @schimi

                                Ich setze die 1.4.9 ein. Seit der 1.4.8 habe ich den u.a. Fehler. Der Datenpunkt liegt nicht vor. Muss er von Hand eingetragen werden?

                                
                                javascript.2	15:15:49.056	error	
                                [Wetter.com] Fehler: ICON_BASE_URL is not defined
                                
                                

                                Gruß
                                Martin


                                Intel NUCs mit Proxmox / Iobroker als VM unter Debian
                                Raspeberry mit USB Leseköpfen für Smartmeter
                                Homematic und Homematic IP

                                1 Antwort Letzte Antwort
                                0
                                • S Offline
                                  S Offline
                                  Schimi
                                  schrieb zuletzt editiert von
                                  #16

                                  versuche mal die hier im Spoiler... bitte um Feedback, dann tausche ich die oben aus

                                  /**
                                  * ioBroker Script: Wetter.com Forecast API v4.0
                                  * API: https://doc.meteonomiqs.com/doc/forecast_v4_0.html
                                  * * Changelog:
                                  * 1.4.11: FIX: Fallback-Werte für Konfigurations-Variablen (ICON_BASE_URL, DEFAULT_LANGUAGE) integriert. 
                                  * Verhindert Abstürze ("ReferenceError"), wenn beim Update der Konfig-Bereich nicht aktualisiert wurde.
                                  * 1.4.10: Feature: Dynamische Spracheinstellung.
                                  * 1.4.8: Icon-URL Anpassung.
                                  * * free API-Key anfordern: https://www.meteonomiqs.com/de/wetter-api/#heading_PricePackages/
                                  */
                                  
                                  // --- KONFIGURATION ---
                                  const API_KEY = 'DEIN_API_KEY_HIER'; // <-- BITTE HIER DEINEN API-KEY EINTRAGEN
                                  const BASE_URL = 'https://forecast.meteonomiqs.com/v4_0';
                                  const ICON_BASE_URL = 'https://cs3.wettercomassets.com/wcomv5/images/icons/weather'; 
                                  const DP_PATH = '0_userdata.0.wetter_com';
                                  const DEFAULT_LANGUAGE = 'de'; // Fallback, falls System-Sprache nicht ermittelbar
                                  const FORECAST_DAYS = 7; 
                                  
                                  // --- STANDORT KONFIGURATION ---
                                  const MANUAL_LATITUDE = ''; 
                                  const MANUAL_LONGITUDE = '';
                                  const FORCE_MANUAL_LOCATION = false; 
                                  
                                  // --- RANDOMISIERUNG DER ZEITEN ---
                                  
                                  function getRandomCron(startHour, endHour, minMinute = 0) {
                                     const hour = Math.floor(Math.random() * (endHour - startHour + 1)) + startHour;
                                     let minute;
                                     if (hour === startHour) {
                                         minute = Math.floor(Math.random() * (60 - minMinute)) + minMinute;
                                     } else if (hour === endHour) {
                                         minute = 0; 
                                     } else {
                                         minute = Math.floor(Math.random() * 60);
                                     }
                                     return `${minute} ${hour} * * *`;
                                  }
                                  
                                  const cron1 = getRandomCron(0, 5, 2);
                                  const cron2 = getRandomCron(13, 17, 2);
                                  
                                  console.log(`[Wetter.com] Schedules für heute gesetzt auf: "${cron1}" und "${cron2}"`);
                                  
                                  // --- HILFSFUNKTIONEN ---
                                  
                                  /**
                                  * Formatiert ein Datum manuell in das Format: "DD.MM.YYYY"
                                  */
                                  function formatDate(dateStr) {
                                     if (!dateStr) return '';
                                     const date = new Date(dateStr);
                                     if (isNaN(date.getTime())) return dateStr;
                                     const day = String(date.getDate()).padStart(2, '0');
                                     const month = String(date.getMonth() + 1).padStart(2, '0');
                                     const year = date.getFullYear();
                                     return `${day}.${month}.${year}`;
                                  }
                                  
                                  /**
                                  * Ermittelt den Wochentag basierend auf der übergebenen Sprache (Locale)
                                  */
                                  function getDayName(dateStr, locale) {
                                     if (!dateStr) return '';
                                     const date = new Date(dateStr);
                                     if (isNaN(date.getTime())) return '';
                                     return date.toLocaleDateString(locale, { weekday: 'long' });
                                  }
                                  
                                  /**
                                  * Ermittelt Einstellungen (Coords + Sprache) aus ioBroker System-Config
                                  */
                                  async function getSystemSettings() {
                                     // Safety check für Variablen, falls User Config nicht aktualisiert hat
                                     const manualLat = (typeof MANUAL_LATITUDE !== 'undefined') ? MANUAL_LATITUDE : '';
                                     const manualLon = (typeof MANUAL_LONGITUDE !== 'undefined') ? MANUAL_LONGITUDE : '';
                                     const forceManual = (typeof FORCE_MANUAL_LOCATION !== 'undefined') ? FORCE_MANUAL_LOCATION : false;
                                     const defLang = (typeof DEFAULT_LANGUAGE !== 'undefined') ? DEFAULT_LANGUAGE : 'de';
                                  
                                     // 1. Manuelle Location?
                                     let coords = null;
                                     if (forceManual && manualLat && manualLon) {
                                          console.log(`[Wetter.com] Nutze manuell konfigurierte Koordinaten (Override).`);
                                          coords = { lat: parseFloat(manualLat).toFixed(3), lon: parseFloat(manualLon).toFixed(3) };
                                     }
                                  
                                     // 2. System Config lesen
                                     const systemConf = await new Promise((resolve) => {
                                         getObject('system.config', (err, obj) => {
                                             if (!err && obj && obj.common) {
                                                 resolve({
                                                     lat: obj.common.latitude ? parseFloat(obj.common.latitude).toFixed(3) : null,
                                                     lon: obj.common.longitude ? parseFloat(obj.common.longitude).toFixed(3) : null,
                                                     lang: obj.common.language || defLang
                                                 });
                                             } else {
                                                 resolve({ lat: null, lon: null, lang: defLang });
                                             }
                                         });
                                     });
                                  
                                     // 3. Fallback Logik
                                     if (!coords) {
                                         if (systemConf.lat && systemConf.lon) {
                                             coords = { lat: systemConf.lat, lon: systemConf.lon };
                                         } else if (manualLat && manualLon) {
                                             console.log('[Wetter.com] Warnung: Keine System-Koordinaten. Nutze Fallback.');
                                             coords = { lat: parseFloat(manualLat).toFixed(3), lon: parseFloat(manualLon).toFixed(3) };
                                         }
                                     }
                                  
                                     if (!coords) return null;
                                  
                                     return { ...coords, lang: systemConf.lang };
                                  }
                                  
                                  async function ensureStructure(path, index, isInfo = false) {
                                     if (isInfo) {
                                         await createStateAsync(`${DP_PATH}.info.last_sync`, '', false, { name: 'Letztes erfolgreiches Update', type: 'string', role: 'text' });
                                         await createStateAsync(`${DP_PATH}.info.requests_today`, 0, false, { name: 'Anfragen heute', type: 'number', role: 'value' });
                                         await createStateAsync(`${DP_PATH}.info.requests_month`, 0, false, { name: 'Anfragen aktueller Monat', type: 'number', role: 'value' });
                                         await createStateAsync(`${DP_PATH}.info.next_schedules`, '', false, { name: 'Geplante Abrufe', type: 'string', role: 'text' });
                                         return;
                                     }
                                  
                                     const states = {
                                         'date': { name: 'Datum', type: 'string', role: 'text', def: '' },
                                         'day_name': { name: 'Wochentag', type: 'string', role: 'text', def: '' },
                                         'temp_max': { name: 'Max Temperatur', type: 'number', unit: '°C', role: 'value.temperature.max', def: 0 },
                                         'temp_min': { name: 'Min Temperatur', type: 'number', unit: '°C', role: 'value.temperature.min', def: 0 },
                                         'weather_text': { name: 'Wetterzustand', type: 'string', role: 'weather.state', def: '' },
                                         'weather_code': { name: 'Wetter Code', type: 'number', role: 'value', def: 0 },
                                         'weather_icon': { name: 'Wetter Icon URL', type: 'string', role: 'weather.icon', def: '' },
                                         'prec_probability': { name: 'Regenrisiko', type: 'number', unit: '%', role: 'value.precipitation.probability', def: 0 },
                                         'prec_sum': { name: 'Regenmenge', type: 'number', unit: 'mm', role: 'value.precipitation', def: 0 },
                                         'sun_hours': { name: 'Sonnenstunden', type: 'number', unit: 'h', role: 'value.sunshine', def: 0 },
                                         'wind_speed_max': { name: 'Windböen Max', type: 'number', unit: 'km/h', role: 'value.speed.wind.gust', def: 0 },
                                         'wind_direction': { name: 'Windrichtung', type: 'string', role: 'weather.direction', def: '' }
                                     };
                                  
                                     for (const [id, config] of Object.entries(states)) {
                                         await createStateAsync(`${path}.${id}`, config.def, false, {
                                             name: `Tag ${index}: ${config.name}`,
                                             type: config.type,
                                             role: config.role,
                                             unit: config.unit || '',
                                             read: true,
                                             write: false
                                         });
                                     }
                                  }
                                  
                                  async function performRequest(url, options) {
                                     return new Promise((resolve, reject) => {
                                         httpGet(url, options, (error, response) => {
                                             if (error) return reject(new Error(error));
                                             if (response.statusCode === 429) return reject(new Error('LIMIT_REACHED'));
                                             if (response.statusCode !== 200) return reject(new Error(`HTTP Status ${response.statusCode}`));
                                             resolve(response);
                                         });
                                     });
                                  }
                                  
                                  async function cleanupObsoleteDays() {
                                     const channels = $(`${DP_PATH}.day_*`);
                                     channels.each(function(id) {
                                         const parts = id.split('.');
                                         const lastPart = parts[parts.length - 1]; 
                                         const dayIndex = parseInt(lastPart.replace('day_', ''));
                                         if (!isNaN(dayIndex) && dayIndex >= FORECAST_DAYS) {
                                             deleteObject(id, true);
                                         }
                                     });
                                  }
                                  
                                  async function updateUsageInfo() {
                                     const now = new Date();
                                     const day = String(now.getDate()).padStart(2, '0');
                                     const month = String(now.getMonth() + 1).padStart(2, '0');
                                     const year = now.getFullYear();
                                     const hours = String(now.getHours()).padStart(2, '0');
                                     const minutes = String(now.getMinutes()).padStart(2, '0');
                                     const seconds = String(now.getSeconds()).padStart(2, '0');
                                     
                                     const timestamp = `${day}.${month}.${year} ${hours}:${minutes}:${seconds}`;
                                     await setStateAsync(`${DP_PATH}.info.last_sync`, String(timestamp), true);
                                     
                                     const currentCountState = await getStateAsync(`${DP_PATH}.info.requests_today`);
                                     const currentCount = currentCountState ? (currentCountState.val || 0) : 0;
                                     await setStateAsync(`${DP_PATH}.info.requests_today`, currentCount + 1, true);
                                  
                                     const currentMonthState = await getStateAsync(`${DP_PATH}.info.requests_month`);
                                     const currentMonthCount = currentMonthState ? (currentMonthState.val || 0) : 0;
                                     await setStateAsync(`${DP_PATH}.info.requests_month`, currentMonthCount + 1, true);
                                     
                                     const s1 = cron1.split(' ');
                                     const s2 = cron2.split(' ');
                                     await setStateAsync(`${DP_PATH}.info.next_schedules`, `${s1[1].padStart(2,'0')}:${s1[0].padStart(2,'0')} Uhr & ${s2[1].padStart(2,'0')}:${s2[0].padStart(2,'0')} Uhr`, true);
                                  }
                                  
                                  // --- LOGIK ---
                                  
                                  async function fetchWeatherData() {
                                     try {
                                         const settings = await getSystemSettings();
                                         if (!settings) {
                                             console.error('[Wetter.com] Fehler: Keine Koordinaten gefunden!');
                                             return;
                                         }
                                         
                                         console.log(`[Wetter.com] Starte Abruf für Lat ${settings.lat}, Lon ${settings.lon} (Sprache: ${settings.lang})`);
                                  
                                         const url = `${BASE_URL}/forecast/${settings.lat}/${settings.lon}/summary`;
                                         const options = { headers: { 'x-api-key': API_KEY, 'Accept-Language': settings.lang } };
                                  
                                         try {
                                             const response = await performRequest(url, options);
                                             const data = JSON.parse(response.data);
                                             if (data && data.items) {
                                                 await processForecastData(data.items, settings.lang);
                                                 await cleanupObsoleteDays();
                                                 await updateUsageInfo();
                                             }
                                         } catch (err) {
                                             if (err.message === 'LIMIT_REACHED') {
                                                 console.error('[Wetter.com] Fehler 429: Monatslimit erreicht.');
                                             } else {
                                                 console.error(`[Wetter.com] Fehler: ${err.message}`);
                                             }
                                         }
                                     } catch (e) {
                                         console.error(`[Wetter.com] Script-Fehler: ${e.message}`);
                                     }
                                  }
                                  
                                  async function processForecastData(items, lang) {
                                     await ensureStructure('', 0, true); 
                                     const daysToProcess = Math.min(items.length, FORECAST_DAYS);
                                  
                                     // Fallback URL, falls Variable durch Teil-Update fehlt
                                     const iconBaseUrl = (typeof ICON_BASE_URL !== 'undefined') 
                                         ? ICON_BASE_URL 
                                         : 'https://cs3.wettercomassets.com/wcomv5/images/icons/weather';
                                  
                                     for (let i = 0; i < daysToProcess; i++) {
                                         const day = items[i];
                                         const dayPath = `${DP_PATH}.day_${i}`;
                                         await ensureStructure(dayPath, i);
                                  
                                         const germanDate = formatDate(day.date);
                                         const dayName = getDayName(day.date, lang);
                                         
                                         const weatherCode = day.weather?.state ?? 0;
                                         const iconUrl = `${iconBaseUrl}/d_${weatherCode}.svg`;
                                  
                                         await setStateAsync(`${dayPath}.date`, germanDate, true);
                                         await setStateAsync(`${dayPath}.day_name`, dayName, true);
                                         await setStateAsync(`${dayPath}.temp_max`, day.temperature?.max ?? 0, true);
                                         await setStateAsync(`${dayPath}.temp_min`, day.temperature?.min ?? 0, true);
                                         await setStateAsync(`${dayPath}.weather_text`, day.weather?.text || '', true);
                                         await setStateAsync(`${dayPath}.weather_code`, weatherCode, true);
                                         await setStateAsync(`${dayPath}.weather_icon`, iconUrl, true);
                                         await setStateAsync(`${dayPath}.prec_probability`, day.prec?.probability ?? 0, true);
                                         await setStateAsync(`${dayPath}.prec_sum`, day.prec?.sum ?? 0, true);
                                         await setStateAsync(`${dayPath}.sun_hours`, day.sunHours ?? 0, true);
                                         await setStateAsync(`${dayPath}.wind_speed_max`, day.wind?.max ?? 0, true);
                                         await setStateAsync(`${dayPath}.wind_direction`, day.wind?.direction || '', true);
                                     }
                                     console.log(`[Wetter.com] Update von ${daysToProcess} Tagen abgeschlossen.`);
                                  }
                                  
                                  schedule("0 0 * * *", () => {
                                     setState(`${DP_PATH}.info.requests_today`, 0, true);
                                  });
                                  
                                  schedule("0 0 1 * *", () => {
                                     setState(`${DP_PATH}.info.requests_month`, 0, true);
                                  });
                                  
                                  schedule(cron1, fetchWeatherData);
                                  schedule(cron2, fetchWeatherData);
                                  
                                  ensureStructure('', 0, true).then(() => {
                                     fetchWeatherData();
                                  });
                                  

                                  1 Antwort Letzte Antwort
                                  1
                                  • M Offline
                                    M Offline
                                    MartyBr
                                    schrieb zuletzt editiert von
                                    #17

                                    Sieht gut. Kein Fehler mehr.
                                    Ich lasse diese Version nun laufen.

                                    Danke für die prompte Fehlerbehebung.

                                    Gruß
                                    Martin


                                    Intel NUCs mit Proxmox / Iobroker als VM unter Debian
                                    Raspeberry mit USB Leseköpfen für Smartmeter
                                    Homematic und Homematic IP

                                    1 Antwort Letzte Antwort
                                    1
                                    Antworten
                                    • In einem neuen Thema antworten
                                    Anmelden zum Antworten
                                    • Älteste zuerst
                                    • Neuste zuerst
                                    • Meiste Stimmen


                                    Support us

                                    ioBroker
                                    Community Adapters
                                    Donate

                                    424

                                    Online

                                    32.6k

                                    Benutzer

                                    82.0k

                                    Themen

                                    1.3m

                                    Beiträge
                                    Community
                                    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                    ioBroker Community 2014-2025
                                    logo
                                    • Anmelden

                                    • Du hast noch kein Konto? Registrieren

                                    • Anmelden oder registrieren, um zu suchen
                                    • Erster Beitrag
                                      Letzter Beitrag
                                    0
                                    • Home
                                    • Aktuell
                                    • Tags
                                    • Ungelesen 0
                                    • Kategorien
                                    • Unreplied
                                    • Beliebt
                                    • GitHub
                                    • Docu
                                    • Hilfe