Skip to main content

MMM-AirQuality – Bug fetch failed / ETIMEDOUT

Symptômes

  • MagicMirror démarre mais log PM2 affiche :

    • TypeError: fetch failed

    • AggregateError [ETIMEDOUT] dans MMM-AirQuality/helper.js

  • curl https://api.waqi.info/... fonctionne


Objectif

👉 Contourner fetch de Node en remplaçant l’appel API par https.request.


Étapes rapides

  1. Aller dans le dossier du module



    cd ~/MagicMirror/modules/MMM-AirQuality
  2. Sauvegarder le helper



    cp helper.js helper.js.bak
  3. Éditer helper.js



    nano helper.js
  4. Remplacer tout le contenu

    /* MagicMirror²
     * Module: MMM-AirQuality
     *
     * By Christopher Fenner https://github.com/CFenner
     * Patched to use https.request instead of fetch (ETIMEDOUT issue).
     * MIT Licensed.
     */
    
    const https = require("https");
    const { URL } = require("url");
    
    module.exports = {
      notifications: {
        DATA: "AIR_QUALITY_DATA",
        DATA_RESPONSE: "AIR_QUALITY_DATA_RESPONSE",
      },
    
      start: function () {
        console.log("AirQuality helper started ...");
      },
    
      loadData: function (payload) {
        const self = this;
        const urlString = `https://${payload.config.apiBase}${payload.config.dataEndpoint}${payload.config.location}/?token=${payload.config.token}`;
    
        console.log(`AirQuality-Fetcher (https): ${urlString}`);
    
        let url;
        try {
          url = new URL(urlString);
        } catch (e) {
          console.error("AirQuality-Fetcher: invalid URL", e);
          self.sendSocketNotification(self.notifications.DATA_RESPONSE, {
            payloadReturn: null,
            status: "ERROR",
            identifier: payload.identifier,
          });
          return;
        }
    
        const options = {
          hostname: url.hostname,
          port: 443,
          path: url.pathname + url.search,
          method: "GET",
          timeout: 10000, // 10s
        };
    
        const req = https.request(options, (res) => {
          let data = "";
    
          res.on("data", (chunk) => {
            data += chunk;
          });
    
          res.on("end", () => {
            try {
              const json = JSON.parse(data);
              self.sendSocketNotification(self.notifications.DATA_RESPONSE, {
                payloadReturn: json,
                status: "OK",
                identifier: payload.identifier,
              });
            } catch (err) {
              console.error("AirQuality-Fetcher: JSON parse error", err);
              self.sendSocketNotification(self.notifications.DATA_RESPONSE, {
                payloadReturn: null,
                status: "ERROR",
                identifier: payload.identifier,
              });
            }
          });
        });
    
        req.on("error", (err) => {
          console.error("AirQuality-Fetcher: https error", err);
          self.sendSocketNotification(self.notifications.DATA_RESPONSE, {
            payloadReturn: null,
            status: "ERROR",
            identifier: payload.identifier,
          });
        });
    
        req.on("timeout", () => {
          console.error("AirQuality-Fetcher: request timeout");
          req.destroy();
          self.sendSocketNotification(self.notifications.DATA_RESPONSE, {
            payloadReturn: null,
            status: "TIMEOUT",
            identifier: payload.identifier,
          });
        });
    
        req.end();
      },
    
      socketNotificationReceived: function (notification, payload) {
        switch (notification) {
          case this.notifications.DATA:
            console.log(
              `AirQuality-Fetcher: Loading data of ${payload.config.location} for module ${payload.identifier}`
            );
            this.loadData(payload);
            break;
        }
      },
    };
    
  5. Redémarrer MagicMirror


    pm2 restart mm
  6. Vérifier

    • Plus d’erreur fetch failed / ETIMEDOUT

    • MMM-AirQuality affiche à nouveau les données


À noter

  • En cas de mise à jour du module, le patch peut être écrasé → réappliquer helper.js patché.