15kilovolt

Magic Mirror configuration file

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