15kilovolt

Untitled

May 18th, 2025 (edited)
635
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JSON 14.97 KB | None | 0 0
  1. /* global Log Module */
  2.  
  3. /* MagicMirror²
  4.  * Module: MMM-DBF
  5.  *
  6.  * By Marc Helpenstein <[email protected]>
  7.  * MIT Licensed.
  8.  */
  9.  
  10. Module.register("MMM-DBF", {
  11.   defaults: {
  12.     updateInterval: 60000, // 1 minute
  13.     retryDelay: 30000, // 30 seconds
  14.     station: "Düsseldorf Hbf",
  15.     platform: "",
  16.     via: "",
  17.     showApp: false,
  18.     showArrivalTime: false,
  19.     showRealTime: false,
  20.     onlyArrivalTime: false,
  21.     numberOfResults: 10,
  22.     hideLowDelay: false,
  23.     withoutDestination: "",
  24.     onlyDestination: "",
  25.     train: "",
  26.     height: "600px",
  27.     width: "400px",
  28.     setTableWidth: "",
  29.     timeOption: "time", // time+countdown or countdown
  30.     showDelayMsg: false,
  31.   },
  32.  
  33.   requiresVersion: "2.1.0",
  34.  
  35.   /**
  36.    * @description Helper function to generate API url
  37.    *
  38.    * @returns {String} url
  39.    */
  40.   generateUrl() {
  41.     this.sendNotification("MMM_DBF_GENERATING_URL", { message: '[MMM-DBF] Generating URL from dbf.finalrewind.org ...'});
  42.     let baseUrl = "https://dbf.finalrewind.org/";
  43.     baseUrl += `${this.config.station}?platforms=${this.config.platform}&via=${this.config.via}&hide_opts=1`;
  44.     if (this.config.showArrivalTime) {
  45.       baseUrl += "&detailed=1";
  46.     }
  47.     if (this.config.showRealTime) {
  48.       baseUrl += "&show_realtime=1";
  49.     }
  50.     if (this.config.onlyArrivalTime) {
  51.       baseUrl += "&admode=dep";
  52.     }
  53.     else {
  54.       baseUrl += "&admode=dep";
  55.     }
  56.     if (this.config.hideLowDelay) {
  57.       baseUrl += "&hidelowdelay=1";
  58.     }
  59.     return baseUrl;
  60.   },
  61.  
  62.   /**
  63.    * @description Calls updateIterval
  64.    */
  65.   start() {
  66.     // Flag for check if module is loaded
  67.     this.loaded = false;
  68.     // Schedule update timer.
  69.     this.getData();
  70.     // Show that the module is running by issuing a notification
  71.     this.sendNotification("MMM_DBF_LOADED", { message: '[MMM-DBF] Module loaded.'});
  72.   },
  73.  
  74.   /**
  75.    * @description Gets data from dbf.finalrewind.org
  76.    */
  77.   async getData() {
  78.     const self = this;
  79.     const urlApi = `${this.generateUrl()}&mode=json&version=3`;
  80.     const dataRequest = await fetch(urlApi);
  81.  
  82.     if (!dataRequest.ok) {
  83.       // Show that the data request is being processed
  84.       this.sendNotification("MMM_DBF_ERROR_PROCESSING_DATA", { message: '[MMM-DBF] Data processing returned an error. See log.'});
  85.       let message = `An error has occurred: ${dataRequest.status}`;
  86.       if (dataRequest.status === 300) {
  87.         message += " - Ambiguous station name.";
  88.       }
  89.       throw new Error(message);
  90.     }
  91.     else {
  92.       const data = await dataRequest.json();
  93.       // Show that the data request is being processed
  94.       this.sendNotification("MMM_DBF_START_PROCESSING_DATA", { message: '[MMM-DBF] Data processing started.'});
  95.       self.processData(data);
  96.     }
  97.     self.scheduleUpdate(self.config.retryDelay);
  98.   },
  99.  
  100.   /**
  101.    * @description Schedule next update.
  102.    * @param {int} delay - Milliseconds before next update.
  103.    */
  104.   scheduleUpdate(delay) {
  105.     const self = this;
  106.     let nextLoad = this.config.updateInterval;
  107.     if (typeof delay !== "undefined" && delay >= 0) {
  108.       nextLoad = delay;
  109.     }
  110.     setTimeout(() => {
  111.       self.getData();
  112.     }, nextLoad);
  113.  
  114.     if (!this.config.showApp) {
  115.       // Show that the module is updating its content
  116.       this.sendNotification("MMM_DBF_UPDATING_DOM", { message: '[MMM-DBF] DOM is being updated.'});
  117.       this.updateDom();
  118.     }
  119.   },
  120.  
  121.   /**
  122.    * @description Create App Frame or HTML table
  123.    *
  124.    * @returns {HTMLIframeElement}
  125.    */
  126.   getDom() {
  127.     if (this.config.showApp) {
  128.       const iframe = document.createElement("IFRAME");
  129.       iframe.style = "border:0";
  130.       iframe.width = this.config.width;
  131.       iframe.height = this.config.height;
  132.       iframe.src = this.generateUrl();
  133.       // Show that the module has set up the iFrame
  134.       this.sendNotification("MMM_DBF_IFRAME_URL_GENERATED", { message: '[MMM-DBF] URL for iFrame successfully generated.'});
  135.       return iframe;
  136.     }
  137.     const tableWrapper = document.createElement("table");
  138.     tableWrapper.className = "small mmm-dbf-table";
  139.     if (this.dataRequest) {
  140.       if (!this.dataRequest.error) {
  141.         if (this.config.setTableWidth) {
  142.           tableWrapper.style.width = this.config.setTableWidth;
  143.         }
  144.         const { departures } = this.dataRequest;
  145.         const tableHead = this.createTableHeader(departures);
  146.         tableWrapper.appendChild(tableHead);
  147.         this.createTableContent(departures, tableWrapper);
  148.         // Table content successfully generated
  149.         this.sendNotification("MMM_DBF_TABLE_CONTENT_GENERATED", { message: '[MMM-DBF] Table content successfully generated.'});
  150.       }
  151.       else {
  152.         // An error occurred which is being logged
  153.         this.sendNotification("MMM_DBF_DATA_REQUEST_ERROR", { message: '[MMM-DBF] Error while performing data request. See log.'});
  154.         Log.error(this.dataRequest.error);
  155.       }
  156.     }
  157.     return tableWrapper;
  158.   },
  159.  
  160.   /**
  161.    * @description Get the size for showing entrys
  162.    * @param {Object[]} departures
  163.    */
  164.   getSize(departures) {
  165.     if (departures.length < this.config.numberOfResults) {
  166.       return departures.length;
  167.     }
  168.     return this.config.numberOfResults;
  169.   },
  170.  
  171.   /**
  172.    * @description Check delay exist
  173.    * @param {Object[]} departures
  174.    */
  175.   checkDelayExist: function (departures) {
  176.     for (let index = 0; index < this.getSize(departures); index++) {
  177.       if (departures[index].delayDeparture) {
  178.         if (this.config.hideLowDelay && departures[index].delayDeparture >= 5) {
  179.           return true;
  180.         }
  181.         if (!this.config.hideLowDelay) {
  182.           return true;
  183.         }
  184.       }
  185.     }
  186.     return false;
  187.   },
  188.  
  189.   /**
  190.    * @description Get col number
  191.    */
  192.   getColDelay() {
  193.     if (this.config.via !== "") {
  194.       return 5;
  195.     }
  196.     return 4;
  197.   },
  198.  
  199.   /**
  200.    * @param {Object} train
  201.    */
  202.   getViaFromRoute(train) {
  203.     const viaConfigList = this.config.via.split(",");
  204.     const route = train.via;
  205.     for (let i = 0; i < route.length; i += 1) {
  206.       const city = route[i];
  207.       for (let j = 0; j < viaConfigList.length; j += 1) {
  208.         if (city.includes(viaConfigList[j])) {
  209.           return viaConfigList[j];
  210.         }
  211.       }
  212.     }
  213.     return false;
  214.   },
  215.  
  216.   /**
  217.    * @description Check if destination is in list config.withoutDestination
  218.    * @param {Object} train
  219.    */
  220.   checkDestination(train, destinationConfig) {
  221.     const destinations = destinationConfig.split(",");
  222.     for (let index = 0; index < destinations.length; index += 1) {
  223.       if (train.destination === destinations[index]) {
  224.         return true;
  225.       }
  226.     }
  227.     return false;
  228.   },
  229.  
  230.   /**
  231.    * @description Check if train is in list config.train
  232.    * @param {Object} train
  233.    */
  234.   checkTrain(train) {
  235.     const trains = this.config.train.split(",");
  236.     const trainName = train.train.split(" ")[0] + train.train.split(" ")[1];
  237.     for (let i = 0; i < trains.length; i += 1) {
  238.       if (trainName.includes(trains[i])) {
  239.         return true;
  240.       }
  241.     }
  242.     return false;
  243.   },
  244.  
  245.   /**
  246.    * @description Checks time and return day/hour/mins
  247.    * @param {int} time - Remaining time
  248.    */
  249.   convertTime(scheduledTime) {
  250.     const time = this.calculateTime(scheduledTime);
  251.     if (time >= 3600) {
  252.       const strTime = Math.floor(time / 3600).toString();
  253.       return `+${strTime} ${this.translate("HOUR")}`;
  254.     }
  255.     if (time >= 60) {
  256.       const strTime = Math.floor(time / 60).toString();
  257.       return `${strTime} ${this.translate("MINUTE")}`;
  258.     }
  259.     return this.translate("NOW");
  260.   },
  261.  
  262.   /**
  263.    * @description Calculate remaining time
  264.    * @param {int:int} scheduledTime
  265.    */
  266.   calculateTime(scheduledTime) {
  267.     const d = new Date();
  268.     const time = scheduledTime.split(":");
  269.     const dateTrain = new Date(
  270.       d.getFullYear(),
  271.       d.getMonth(),
  272.       d.getDate(),
  273.       time[0],
  274.       time[1],
  275.     );
  276.     const newStamp = new Date().getTime();
  277.     return Math.round((dateTrain.getTime() - newStamp) / 1000);
  278.   },
  279.  
  280.   /**
  281.    * @description Check msg exists
  282.    * @param {Object[]} departures
  283.    */
  284.   checkMsgExist(departures) {
  285.     for (let index = 0; index < this.getSize(departures); index += 1) {
  286.       if (
  287.         departures[index] !== undefined
  288.         && departures[index].messages.delay.length > 0
  289.       ) {
  290.         return true;
  291.       }
  292.     }
  293.     return false;
  294.   },
  295.  
  296.   /**
  297.    * @description Creates the header for the Table
  298.    */
  299.   createTableHeader(departures) {
  300.     const tableHead = document.createElement("tr");
  301.     tableHead.className = "border-bottom";
  302.  
  303.     const tableHeadValues = [
  304.       this.translate("TRAIN"),
  305.       this.translate("TRACK"),
  306.       this.translate("DESTINATION"),
  307.     ];
  308.  
  309.     if (this.config.via !== "") {
  310.       tableHeadValues.push(this.translate("VIA"));
  311.     }
  312.     if (!this.config.onlyArrivalTime) {
  313.       tableHeadValues.push(this.translate("DEPARTURE"));
  314.     }
  315.     else {
  316.       tableHeadValues.push(this.translate("ARRIVAL"));
  317.     }
  318.  
  319.     if (
  320.       this.checkDelayExist(departures)
  321.       || this.checkCancelledExist(departures)
  322.     ) {
  323.       const delayClockIcon = "<i class=\"fa fa-clock-o\"></i>";
  324.       tableHeadValues.push(delayClockIcon);
  325.     }
  326.  
  327.     if (this.config.showDelayMsg && this.checkMsgExist(departures)) {
  328.       tableHeadValues.push(this.translate("DELAYMSG"));
  329.     }
  330.  
  331.     for (
  332.       let thCounter = 0;
  333.       thCounter < tableHeadValues.length;
  334.       thCounter += 1
  335.     ) {
  336.       const tableHeadSetup = document.createElement("th");
  337.       if (thCounter === 5) {
  338.         tableHeadSetup.style.textAlign = "Left";
  339.       }
  340.       tableHeadSetup.innerHTML = tableHeadValues[thCounter];
  341.       tableHead.appendChild(tableHeadSetup);
  342.     }
  343.     return tableHead;
  344.   },
  345.  
  346.   /**
  347.    * @param usableResults
  348.    * @param tableWrapper
  349.    * @returns {HTMLTableRowElement}
  350.    */
  351.   createTableContent(departures, tableWrapper) {
  352.     // Show the generation of the content has started
  353.     this.sendNotification("MMM_DBF_GENERATING_TABLE_CONTENT", { message: '[MMM-DBF] Table content is being generated.'});
  354.     let size = this.getSize(departures);
  355.     let departureCount = 0;
  356.     for (let index = 0; index < size; index += 1) {
  357.       const obj = departures[index];
  358.       const trWrapper = document.createElement("tr");
  359.       trWrapper.className = obj.isCancelled ? "tr cancelled" : "tr";
  360.       this.checkMsgExist(obj);
  361.  
  362.       // Check train
  363.       if (this.config.train !== "" && !this.checkTrain(obj)) {
  364.         if (size + 1 <= departures.length) {
  365.           size += 1;
  366.         }
  367.       }
  368.       else if (
  369.         this.config.withoutDestination !== ""
  370.         && this.checkDestination(obj, this.config.withoutDestination)
  371.       ) {
  372.         if (size + 1 <= departures.length) {
  373.           size += 1;
  374.         }
  375.       }
  376.       else if (
  377.         this.config.onlyDestination !== ""
  378.         && !this.checkDestination(obj, this.config.onlyDestination)
  379.       ) {
  380.         if (size + 1 <= departures.length) {
  381.           size += 1;
  382.         }
  383.       }
  384.       else {
  385.         const tdValues = [obj.train, obj.platform, obj.destination];
  386.         if (this.config.via !== "") {
  387.           const via = this.getViaFromRoute(obj);
  388.           if (via === false) {
  389.             tdValues.push("");
  390.           }
  391.           else {
  392.             tdValues.push(this.getViaFromRoute(obj));
  393.           }
  394.         }
  395.  
  396.         let time;
  397.         if (this.config.onlyArrivalTime) {
  398.           time = obj.scheduledArrival;
  399.         }
  400.         else {
  401.           time = obj.scheduledDeparture;
  402.         }
  403.  
  404.         const remainingTime = this.convertTime(time);
  405.         switch (this.config.timeOption) {
  406.           case "time+countdown":
  407.             tdValues.push(`${time} (${remainingTime})`);
  408.             break;
  409.           case "countdown":
  410.             tdValues.push(remainingTime);
  411.             break;
  412.           default:
  413.             tdValues.push(time);
  414.             break;
  415.         }
  416.  
  417.         if (
  418.           this.checkDelayExist(departures)
  419.           || this.checkCancelledExist(departures)
  420.         ) {
  421.           if (obj.delayDeparture > 0 && !this.config.hideLowDelay) {
  422.             let delay = " +" + obj.delayDeparture;
  423.             tdValues.push(delay);
  424.           }
  425.           else if (obj.delayDeparture >= 5) {
  426.             let delay = " +" + obj.delayDeparture;
  427.             tdValues.push(delay);
  428.           }
  429.           else if (obj.isCancelled > 0) {
  430.             tdValues.push(this.translate("CANCELMSG"));
  431.           }
  432.         }
  433.  
  434.         if (
  435.           this.config.showDelayMsg
  436.           && this.checkMsgExist(departures)
  437.           && obj.delayDeparture > 0
  438.         ) {
  439.           if (obj.messages.delay.length > 0) {
  440.             tdValues.push(obj.messages.delay[0].text);
  441.           }
  442.         }
  443.  
  444.         departureCount += 1;
  445.         for (let c = 0; c < tdValues.length; c += 1) {
  446.           const tdWrapper = document.createElement("td");
  447.           tdWrapper.innerHTML = tdValues[c];
  448.  
  449.           if (c === this.getColDelay()) {
  450.             tdWrapper.className = "delay";
  451.           }
  452.           if (c === this.getColDelay() + 1) {
  453.             tdWrapper.className = "delay";
  454.             tdWrapper.style.width = "200px";
  455.             tdWrapper.style.textAlign = "Left";
  456.             // tdWrapper.innerHTML = '<marquee scrollamount="3" >' + tdValues[c] + '<marquee>';
  457.           }
  458.           trWrapper.appendChild(tdWrapper);
  459.         }
  460.         tableWrapper.appendChild(trWrapper);
  461.       }
  462.     }
  463.  
  464.     if (departureCount === 0) {
  465.       const trWrapper = document.createElement("tr");
  466.       trWrapper.className = "tr";
  467.       const tdWrapper = document.createElement("td");
  468.  
  469.       if (this.config.onlyDestination !== "" && this.config.train !== "") {
  470.         tdWrapper.innerHTML = "Destination or train not found";
  471.         Log.error("Destination or train not found");
  472.       }
  473.       else if (this.config.onlyDestination !== "") {
  474.         tdWrapper.innerHTML = "Destination not found";
  475.         Log.error("Destination not found");
  476.       }
  477.       else if (this.config.train !== "") {
  478.         tdWrapper.innerHTML = "Train not found";
  479.         Log.error("Train not found");
  480.       }
  481.  
  482.       trWrapper.appendChild(tdWrapper);
  483.       tableWrapper.appendChild(trWrapper);
  484.     }
  485.   },
  486.  
  487.   /**
  488.    * @description Define required styles.
  489.    * @returns {[string,string]}
  490.    */
  491.   getStyles() {
  492.     return ["MMM-DBF.css", "font-awesome.css"];
  493.   },
  494.  
  495.   /**
  496.    * @description Load translations files
  497.    * @returns {{en: string, de: string}}
  498.    */
  499.   getTranslations() {
  500.     return {
  501.       en: "translations/en.json",
  502.       de: "translations/de.json",
  503.     };
  504.   },
  505.   /**
  506.    * @description Update data and send notification to node_helper
  507.    * @param {*} data
  508.    */
  509.   processData(data) {
  510.     // Show that data are being processed
  511.     this.sendNotification("MMM_DBF_PROCESSING_DATA", { message: '[MMM-DBF] Data are being processed.'});
  512.     this.dataRequest = data;
  513.  
  514.     if (this.loaded === false) {
  515.       this.updateDom(this.config.animationSpeed);
  516.     }
  517.     this.loaded = true;
  518.   },
  519. });
  520.  
Advertisement
Add Comment
Please, Sign In to add comment