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
    15
    1
    849

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

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    1.9k

[Skript] Wetter.com Forecast/Vorhersage

Geplant Angeheftet Gesperrt Verschoben JavaScript
4 Beiträge 3 Kommentatoren 124 Aufrufe 6 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 Online
    S Online
    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.

    /**
    * ioBroker Script: Wetter.com Forecast API v4.0
    * API: https://doc.meteonomiqs.com/doc/forecast_v4_0.html
    * * Changelog:
    * 1.4.6: Feature: Manuelle Standort-Konfiguration (Fallback & Override) hinzugefügt.
    * 1.4.5: FIX: Syntax-Fehler im Header behoben.
    * 1.4.4: Anpassung an Monatslimit (100 Requests).
    * 1.4.3: Korrektur "last_sync" Formatierung.
    * 1.4.1: Randomisierung der Abrufzeiten.
    * 1.4.0: Info-Datenpunkte hinzugefügt.
    * * 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 DP_PATH = '0_userdata.0.wetter_com';
    const LANGUAGE = 'de-de';
    const FORECAST_DAYS = 7; 
    
    // --- STANDORT KONFIGURATION ---
    // Hier Koordinaten eintragen für Fallback oder spezifischen Standort (z.B. '52.520')
    const MANUAL_LATITUDE = ''; 
    const MANUAL_LONGITUDE = '';
    
    // true  = Nutze IMMER die manuellen Koordinaten (Ignoriert ioBroker-Einstellungen)
    // false = Nutze manuelle Koordinaten nur als Fallback, falls im System keine hinterlegt sind
    const FORCE_MANUAL_LOCATION = false; 
    
    // --- RANDOMISIERUNG DER ZEITEN ---
    
    /**
    * Erzeugt eine zufällige Cron-Zeit innerhalb eines Fensters
    * @param {number} startHour 
    * @param {number} endHour 
    * @param {number} minMinute (optional, z.B. 2 für 00:02)
    */
    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} * * *`;
    }
    
    // Zeitfenster 1: 00:02 bis 05:00
    const cron1 = getRandomCron(0, 5, 2);
    // Zeitfenster 2: 13:02 bis 17:00
    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 deutsches Format: "DD.MM.YYYY"
    */
    function formatToGermanDate(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 die Koordinaten basierend auf Konfiguration und System-Einstellungen
    */
    async function getCoordinates() {
       // 1. Override: Manuelle Koordinaten erzwingen
       if (FORCE_MANUAL_LOCATION && MANUAL_LATITUDE && MANUAL_LONGITUDE) {
            console.log(`[Wetter.com] Nutze manuell konfigurierte Koordinaten (Override).`);
            return { lat: parseFloat(MANUAL_LATITUDE).toFixed(3), lon: parseFloat(MANUAL_LONGITUDE).toFixed(3) };
       }
    
       // 2. System: Versuche ioBroker Einstellungen zu lesen
       const systemCoords = await new Promise((resolve) => {
           getObject('system.config', (err, obj) => {
               if (!err && obj && obj.common && obj.common.latitude && obj.common.longitude) {
                   resolve({
                       lat: parseFloat(obj.common.latitude).toFixed(3),
                       lon: parseFloat(obj.common.longitude).toFixed(3)
                   });
               } else {
                   resolve(null);
               }
           });
       });
    
       if (systemCoords) return systemCoords;
    
       // 3. Fallback: Nutze manuelle Koordinaten, falls System fehlschlug
       if (MANUAL_LATITUDE && MANUAL_LONGITUDE) {
            console.log('[Wetter.com] Warnung: Keine System-Koordinaten gefunden. Nutze Fallback-Koordinaten aus Skript.');
            return { lat: parseFloat(MANUAL_LATITUDE).toFixed(3), lon: parseFloat(MANUAL_LONGITUDE).toFixed(3) };
       }
    
       return null;
    }
    
    /**
    * Erstellt die Struktur inkl. Info-Datenpunkten
    */
    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: '' },
           '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 },
           '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)) {
           const fullId = `${path}.${id}`;
           await createStateAsync(fullId, 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);
           }
       });
    }
    
    /**
    * Aktualisiert die Info-Datenpunkte (Zähler und Zeitstempel)
    */
    async function updateUsageInfo() {
       const now = new Date();
       
       // Manuelle Formatierung
       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);
       
       // Tageszähler erhöhen
       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);
    
       // Monatszähler erhöhen
       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 coords = await getCoordinates();
           if (!coords) {
               console.error('[Wetter.com] Fehler: Keine Koordinaten gefunden (Weder System noch Fallback)!');
               return;
           }
           
           const url = `${BASE_URL}/forecast/${coords.lat}/${coords.lon}/summary`;
           const options = { headers: { 'x-api-key': API_KEY, 'Accept-Language': LANGUAGE } };
    
           try {
               const response = await performRequest(url, options);
               const data = JSON.parse(response.data);
               if (data && data.items) {
                   await processForecastData(data.items);
                   await cleanupObsoleteDays();
                   await updateUsageInfo();
               }
           } catch (err) {
               if (err.message === 'LIMIT_REACHED') {
                   console.error('[Wetter.com] Fehler 429: Das monatliche Abruflimit (100 Anfragen) wurde erreicht.');
               } else {
                   console.error(`[Wetter.com] Fehler: ${err.message}`);
               }
           }
       } catch (e) {
           console.error(`[Wetter.com] Script-Fehler: ${e.message}`);
       }
    }
    
    async function processForecastData(items) {
       await ensureStructure('', 0, true); 
       const daysToProcess = Math.min(items.length, FORECAST_DAYS);
    
       for (let i = 0; i < daysToProcess; i++) {
           const day = items[i];
           const dayPath = `${DP_PATH}.day_${i}`;
           await ensureStructure(dayPath, i);
    
           await setStateAsync(`${dayPath}.date`, formatToGermanDate(day.date), 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`, day.weather?.state ?? 0, 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.`);
    }
    
    // Zähler jeden Tag um Mitternacht zurücksetzen
    schedule("0 0 * * *", () => {
       setState(`${DP_PATH}.info.requests_today`, 0, true);
    });
    
    // Zähler jeden Monat (am 1. um 00:00) zurücksetzen
    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
    3
    • Pedder007P Online
      Pedder007P Online
      Pedder007
      schrieb 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 Online
        sigi234S Online
        sigi234
        Forum Testing Most Active
        schrieb 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
        0
        • Pedder007P Pedder007

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

          S Online
          S Online
          Schimi
          schrieb zuletzt editiert von
          #4

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

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

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


          Support us

          ioBroker
          Community Adapters
          Donate

          832

          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