diff --git a/CHANGELOG.md b/CHANGELOG.md index 79a9746..938763f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [1.0.7] - 2019-11-10 +- fix a problem with loading plugin more then once +- fix problem with counting visible stations +- change MVG API URL +- fix some typos + ## [1.0.6] - 2019-09-17 - add option to show route for regional bus diff --git a/mvgmunich.js b/mvgmunich.js index c5b73f1..dc0aec7 100755 --- a/mvgmunich.js +++ b/mvgmunich.js @@ -1,208 +1,228 @@ -/* Timetable for public transport in Munich */ - -/* - * Magic Mirror - * Module: MVG Munich - * - * By Simon Crnko - * MIT Licensed - * - */ - -const MS_PER_MINUTE = 60000; -const mvgAPI = "https://www.mvg.de/api"; -Module.register("mvgmunich", { - // Default module configuration - defaults: { - maxEntries: 8, // maximum number of results shown on UI - updateInterval: MS_PER_MINUTE, // update every 60 seconds - apiBase: mvgAPI + "/fahrinfo/departure/", - stationQuery: mvgAPI + "/fahrinfo/location/queryWeb?q=", - haltestelle: "Hauptbahnhof", // default departure station - haltestelleId: 0, - haltestelleName: "", - ignoreStations: [], // list of destination to be ignored in the list - timeToWalk: 0, // walking time to the station - showWalkingTime: false, // if the walking time should be included and the starting time is displayed - showTrainDepartureTime: true, - trainDepartureTimeFormat: "relative", - walkingTimeFormat: "relative", - showIcons: true, - transportTypesToShow: { - "ubahn": true, - "sbahn": true, - "regional_bus": true, - "bus": true, - "tram": true - } - }, - - getStyles: function() { - return ["mvgmunich.css"]; - }, - - // Load translations files - getTranslations: function() { - return { - en: "translations/en.json", - de: "translations/de.json" - }; - }, - - start: function() { - var self = this; - Log.info("Starting module: " + this.name); - this.loaded = false; - this.getData(); - setInterval(function() { - self.updateDom(); - }, this.config.updateInterval); - }, - - /* - * getData - * function call getData function in node_helper.js - */ - getData: function() { - // get first stationId based on station name - this.sendSocketNotification("GETSTATION", this.config); - }, - - // Override dom generator. - getDom: function() { - var wrapperTable = document.createElement("div"); - if (this.config.haltestelle === "") { - wrapperTable.className = "dimmed light small"; - wrapperTable.innerHTML = "Please set value for 'Haltestelle'."; - } - if (!this.loaded) { - wrapperTable.className = "dimmed light small"; - wrapperTable.innerHTML = "Loading data from MVG ..."; - return wrapperTable; - } - var wrapperTable = document.createElement("table"); - wrapperTable.className = "small"; - wrapperTable.innerHTML = this.dataRequest; - return wrapperTable; - }, - - getHtml: function(jsonObject) { - var htmlText = ""; - - for (var i = 0, len = jsonObject.departures.length; i < this.config.maxEntries; i++) { - // get one item from api result - var apiResultItem = jsonObject.departures[i]; - // get transport type - var transportType = apiResultItem.product.toLocaleLowerCase(); - - // check if we should show data of this transport type - // check if current station is not part of the ignore list - if (!this.config.transportTypesToShow[transportType] || - this.config.ignoreStations.includes(apiResultItem.destination)) { - continue; - } - - htmlText += ""; - // check if user want's icons - htmlText += this.showIcons(apiResultItem.product, this.config.showIcons); - // add transport number - htmlText += "" + apiResultItem.label + ""; - // add last station aka direction - htmlText += "" + apiResultItem.destination + ""; - // check if user want's to see departure time - htmlText += this.showDepartureTime(apiResultItem.departureTime, this.config); - // check if user want's to see walking time - htmlText += this.showWalkingTime(apiResultItem.departureTime); - htmlText += ""; - } - - return htmlText; - }, - - showIcons(product, showIcons) { - // if (Object.is(showIcons, true)) { - if(showIcons) - return ""; - return ""; - }, - - showWalkingTime: function (departureTime) { - var htmlText = ""; - if (this.config.showWalkingTime) { - htmlText += " / "; - var startWalkingTime = new Date(departureTime - this.config.timeToWalk * MS_PER_MINUTE); - // check what kind of walking time user wants (absolute / relative) - if(this.config.walkingTimeFormat == "absolute") { - htmlText += this.getAbsoluteTime(startWalkingTime); - } else if (this.config.walkingTimeFormat == "relative") { - htmlText += this.getRelativeTime(startWalkingTime); - } else { - htmlText += "walkingTimeFormat config is wrong" - } - htmlText += ""; - } - return htmlText; - }, - - showDepartureTime: function (departureTime, config) { - var htmlText = ""; - if(config.showTrainDepartureTime) { - // add departure time - htmlText += ""; - var departureTime = new Date(departureTime) - - // check what kind of time user wants (absolute / relative) - if(config.trainDepartureTimeFormat == "absolute") { - htmlText += this.getAbsoluteTime(departureTime); - } else if (config.trainDepartureTimeFormat == "relative") { - htmlText += this.getRelativeTime(departureTime); - } else { - htmlText += "trainDepartureTimeFormat config is wrong" - } - htmlText += ""; - } - return htmlText; - }, - - getAbsoluteTime: function(time) { - var hoursStr = (time.getHours() < 10 ? '0' : '') + time.getHours(); - var minutesStr = (time.getMinutes() < 10 ? '0' : '') + time.getMinutes(); - - return hoursStr + ":" + minutesStr; - }, - - getRelativeTime: function(time) { - var timingForStartWalking = Math.floor((time.getTime() - new Date().getTime()) / 1000 / 60); - return (timingForStartWalking <=0 - ? this.translate("JETZT") - : this.translate("IN") + " " + timingForStartWalking + " " + this.translate("MIN")); - }, - - // Override getHeader method. - getHeader: function() { - return this.data.header + " Munich: " + this.config.haltestelleName; - }, - - socketNotificationReceived: function(notification, payload) { - if (notification === "UPDATE") { - this.dataRequest = this.getHtml(payload); - this.loaded = true; - this.updateDom(); - } - if (notification === "ERROR") { - this.dataRequest = payload; - this.loaded = true; - this.updateDom(); - } - if (notification === "ERROR_NO_STATION") { - this.dataRequest = this.translate("NO_STATION"); - this.loaded = true; - this.updateDom(); - } - if (notification === "STATION") { - this.config.haltestelleName = payload.name; - this.config.haltestelleId = payload.id - this.sendSocketNotification("GETDATA", this.config); - } - } -}); +/* Timetable for public transport in Munich */ + +/* + * Magic Mirror + * Module: MVG Munich + * + * By Simon Crnko + * MIT Licensed + * + */ + +const MS_PER_MINUTE = 60000; +const mvgAPI = "https://www.mvg.de/api"; +Module.register("mvgmunich", { + // Default module configuration + defaults: { + maxEntries: 8, // maximum number of results shown on UI + updateInterval: MS_PER_MINUTE, // update every 60 seconds + apiBase: mvgAPI + "/fahrinfo/departure/", + stationQuery: mvgAPI + "/fahrinfo/location/queryWeb?q=", + haltestelle: "Hauptbahnhof", // default departure station + haltestelleId: 0, + haltestelleName: "", + ignoreStations: [], // list of destination to be ignored in the list + timeToWalk: 0, // walking time to the station + showWalkingTime: false, // if the walking time should be included and the starting time is displayed + showTrainDepartureTime: true, + trainDepartureTimeFormat: "relative", + walkingTimeFormat: "relative", + showIcons: true, + transportTypesToShow: { + "ubahn": true, + "sbahn": true, + "regional_bus": true, + "bus": true, + "tram": true + } + }, + + getStyles: function () { + return ["mvgmunich.css"]; + }, + + // Load translations files + getTranslations: function () { + return { + en: "translations/en.json", + de: "translations/de.json" + }; + }, + + start: function () { + this.resultData = []; + Log.info("Starting module: " + this.name + ", identifier: " + this.identifier); + if (this.config.haltestelle !== "") { + this.sendSocketNotification("GET_STATION_INFO", this.config); + } + }, + + /* + * getData + * function call getData function in node_helper.js + * + */ + getData: function () { + const self = this; + self.sendSocketNotification("GET_DEPARTURE_DATA", self.config); + setInterval(function () { + self.sendSocketNotification("GET_DEPARTURE_DATA", self.config); + }, self.config.updateInterval); + }, + + // Override dom generator. + getDom: function () { + let wrapperTable = document.createElement("div"); + if (this.config.haltestelle === "") { + wrapperTable.className = "dimmed light small"; + wrapperTable.innerHTML = "Please set value for 'Haltestelle'."; + return wrapperTable; + } + // console.log("this.resultData: {}", this.resultData); + if (this.resultData === []) { + wrapperTable.className = "dimmed light small"; + wrapperTable.innerHTML = "Loading data from MVG ..."; + return wrapperTable; + } + wrapperTable = document.createElement("table"); + wrapperTable.className = "small"; + wrapperTable.innerHTML = this.resultData[this.config.haltestelle]; + return wrapperTable; + }, + + getHtml: function (jsonObject) { + let htmlText = ""; + + // console.log("payload.maxEntries: " + payload.maxEntries); + let visibleLines = 0; + for (let i = 0; i < jsonObject.departures.length; i++) { + if (visibleLines >= this.config.maxEntries) { + break; + } + // get one item from api result + const apiResultItem = jsonObject.departures[i]; + // get transport type + const transportType = apiResultItem.product.toLocaleLowerCase(); + + // console.log("transportType: " + transportType); + // console.log("apiResultItem.destination: " + apiResultItem.destination); + // console.log("apiResultItem.departureTime: " + apiResultItem.departureTime); + + // check if we should show data of this transport type + // check if current station is not part of the ignore list + if (!this.config.transportTypesToShow[transportType] || + this.config.ignoreStations.includes(apiResultItem.destination)) { + continue; + } + + htmlText += ""; + // check if user want's icons + htmlText += this.showIcons(apiResultItem.product, this.config.showIcons); + // add transport number + htmlText += "" + apiResultItem.label + ""; + // add last station aka direction + htmlText += "" + apiResultItem.destination + ""; + // check if user want's to see departure time + htmlText += this.showDepartureTime(apiResultItem.departureTime); + // check if user want's to see walking time + htmlText += this.showWalkingTime(apiResultItem.departureTime); + htmlText += ""; + visibleLines++; + } + // console.log("htmlText: " + "haltestelle: " + payload.haltestelle + " - " + htmlText); + return htmlText; + }, + + showIcons: function (product, showIcons) { + // console.log("Show icons: ", showIcons); + let icons = ""; + if (showIcons) { + icons = ""; + } + // console.log("Icons content: {}", icons); + return icons; + }, + + showWalkingTime: function (departureTime) { + let htmlText = ""; + if (this.config.showWalkingTime) { + htmlText += " / "; + const startWalkingTime = new Date(departureTime - this.config.timeToWalk * MS_PER_MINUTE); + // check what kind of walking time user wants (absolute / relative) + if (this.config.walkingTimeFormat === "absolute") { + htmlText += this.getAbsoluteTime(startWalkingTime); + } else if (this.config.walkingTimeFormat === "relative") { + htmlText += this.getRelativeTime(startWalkingTime); + } else { + htmlText += "walkingTimeFormat config is wrong"; + } + htmlText += ""; + } + return htmlText; + }, + + showDepartureTime: function (departureTime) { + let htmlText = ""; + if (this.config.showTrainDepartureTime) { + // add departure time + htmlText += ""; + const departureDate = new Date(departureTime); + // console.log("departureDate: " + departureDate); + // check what kind of time user wants (absolute / relative) + if (this.config.trainDepartureTimeFormat === "absolute") { + htmlText += this.getAbsoluteTime(departureDate); + } else if (this.config.trainDepartureTimeFormat === "relative") { + htmlText += this.getRelativeTime(departureDate); + } else { + htmlText += "trainDepartureTimeFormat config is wrong"; + } + htmlText += ""; + } + return htmlText; + }, + + getAbsoluteTime: function (time) { + let hoursStr = (time.getHours() < 10 ? "0" : "") + time.getHours(); + let minutesStr = (time.getMinutes() < 10 ? "0" : "") + time.getMinutes(); + + return hoursStr + ":" + minutesStr; + }, + + getRelativeTime: function (time) { + const timingForStartWalking = Math.floor((time.getTime() - new Date().getTime()) / 1000 / 60); + return (timingForStartWalking <= 0 + ? this.translate("JETZT") + : this.translate("IN") + " " + timingForStartWalking + " " + this.translate("MIN")); + }, + + // Override getHeader method. + getHeader: function () { + if (this.config.haltestelle !== "" || this.config.haltestelleName !== "") { + return this.data.header + " Munich: " + + (this.config.haltestelleName === "" ? this.config.haltestelle : this.config.haltestelleName); + } + return ""; + }, + + socketNotificationReceived: function (notification, payload) { + // console.log("Notification in mvgmunich: " + notification + " - " + payload); + // console.log("this.config.haltestelle: " + this.config.haltestelle); + // console.log("payload.transport: " + payload.transport); + switch (notification) { + case "UPDATE_DEPARTURE_INFO": + this.resultData[payload.haltestelle] = this.getHtml(payload.transport); + break; + case "UPDATE_STATION": + if (this.config.haltestelle === payload.haltestelle) { + this.config.haltestelleId = payload.haltestelleId; + this.config.haltestelleName = payload.haltestelleName; + } + this.getHeader(); + this.getData(); + break; + default: + Log.error(); + } + this.updateDom(); + } +}); diff --git a/node_helper.js b/node_helper.js index 17b4cf1..2af1a36 100755 --- a/node_helper.js +++ b/node_helper.js @@ -7,10 +7,10 @@ * */ -var NodeHelper = require("node_helper"); -var request = require("request"); -var urlencode = require('urlencode'); -var globals = { +const NodeHelper = require("node_helper"); +const request = require("request"); +const urlencode = require("urlencode"); +const globals = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36", "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json, text/javascript, */*; q=0.01", @@ -23,82 +23,83 @@ var globals = { module.exports = NodeHelper.create({ - start: function() { - this.config = null; - }, - - socketNotificationReceived: function(notification, payload) { + socketNotificationReceived: function (notification, payload) { + // console.log("Notification in node_helper: " + notification + " - " + payload); const self = this; - this.config = payload; - if (notification === "GETDATA") { - self.getDepartureInfo(); - self.scheduleUpdate(this.config.updateInterval); - } - if (notification === "GETSTATION") { - self.getStationInfo(); + switch (notification) { + case "GET_STATION_INFO": + self.getStationInfo(payload); + break; + case "GET_DEPARTURE_DATA": + self.getDepartureInfo(payload); + break; + default: + console.error("Switch item {} is missing", notification); } }, - getDepartureInfo: function() { - var self = this; - + getDepartureInfo: function (payload) { + const self = this; + // console.log("Haltestelle: {} URL: {}", payload.haltestelle, payload.apiBase + payload.haltestelleId + "?footway=" + payload.timeToWalk); request({ headers: globals, - uri: self.config.apiBase + self.config.haltestelleId + "?footway=" + self.config.timeToWalk, + uri: payload.apiBase + payload.haltestelleId + "?footway=" + payload.timeToWalk, method: "GET", gzip: true - }, function(error, response, body) { + }, function (error, response, body) { + // console.log("Response: " + response); + // console.log("Body: " + body); if (error) { // Error while reading departure data ... + console.error("Error while reading departure info", error); self.sendSocketNotification("ERROR", "Error while reading data: " + error.message); } else { // body is the decompressed response body - var jsonObject = JSON.parse(body); - self.sendSocketNotification("UPDATE", jsonObject); + try { + // console.log("haltestelle: {} Body: {}", payload.haltestelle, body); + // console.log("haltestelle: {} jsonObject: {}", payload.haltestelle, jsonObject); + // payload.transport = self.getHtml(JSON.parse(body), payload); + payload.transport = JSON.parse(body); + self.sendSocketNotification("UPDATE_DEPARTURE_INFO", payload); + } catch (e) { + console.error("Error while parsing and sending departure info", e); + self.sendSocketNotification("ERROR_NO_DEPARTURE_DATA", ""); + } } }); }, - getStationInfo: function() { - var self = this; - + getStationInfo: function (payload) { + const self = this; + // console.log("Station info Query: {}", payload.stationQuery + urlencode(payload.haltestelle)); request({ headers: globals, - uri: self.config.stationQuery + urlencode(self.config.haltestelle), + uri: payload.stationQuery + urlencode(payload.haltestelle), method: "GET", gzip: true - }, function(error, response, body) { + }, function (error, response, body) { if (error) { - // Error while reading departure data ... + // Error while reading station data ... + console.error("Error while reading station data", error); self.sendSocketNotification("ERROR", "Error while reading data: " + error.message); } else { // body is the decompressed response body try { - var jsonObject = JSON.parse(body); - if(jsonObject.locations[0].id === undefined) { + const jsonObject = JSON.parse(body); + if (jsonObject.locations[0].id === undefined) { self.sendSocketNotification("ERROR_NO_STATION", ""); - } else - self.sendSocketNotification("STATION", jsonObject.locations[0]); + } else { + // console.log("json: {}", jsonObject.locations[0]); + payload.haltestelleId = jsonObject.locations[0].id; + payload.haltestelleName = jsonObject.locations[0].name; + self.sendSocketNotification("UPDATE_STATION", payload); + } } catch (e) { - self.sendSocketNotification("ERROR_NO_STATION", ""); + console.error("Error while parsing and sending station info", e); + self.sendSocketNotification("ERROR_NO_STATION", ""); } } }); }, - /* scheduleUpdate() - * Schedule next update. - * argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used. - */ - scheduleUpdate: function(delay) { - var nextLoad = this.config.updateInterval; - if (typeof delay !== "undefined" && delay >= 0) { - nextLoad = delay; - } - nextLoad = nextLoad; - var self = this; - setInterval(function() { - self.getDepartureInfo(); - }, nextLoad); - } }); diff --git a/package.json b/package.json index 25a2bc0..ffb67a0 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,24 @@ { - "name": "MMM-mvgmunich", - "version": "1.0.6", - "description": "", - "main": "mvgmunich.js", - "author": "Simon Crnko", - "license": "MIT", - "homepage": "https://github.com/mrVragec/MMM-mvgmunich", - "keywords": [ - "magic mirror", - "public transport", - "mvv", - "mvg" - ], - "repository": { - "type": "git", - "url": "https://github.com/mrVragec/MMM-mvgmunich" - }, - "contributors": "", - "dependencies": { - "urlencode":"~1.1.0" - } + "name": "MMM-mvgmunich", + "version": "1.0.7", + "description": "", + "main": "mvgmunich.js", + "author": "Simon Crnko", + "license": "MIT", + "homepage": "https://github.com/mrVragec/MMM-mvgmunich", + "keywords": [ + "magic mirror", + "public transport", + "mvv", + "mvg" + ], + "repository": { + "type": "git", + "url": "https://github.com/mrVragec/MMM-mvgmunich" + }, + "contributors": "", + "dependencies": { + "request": "^2.88.0", + "urlencode": "~1.1.0" + } }