diff --git a/.github/Example_1.png b/.github/Example_1.png new file mode 100644 index 0000000..0ae25b1 Binary files /dev/null and b/.github/Example_1.png differ diff --git a/.github/Example_2.png b/.github/Example_2.png new file mode 100644 index 0000000..697c4ca Binary files /dev/null and b/.github/Example_2.png differ diff --git a/.github/Example_3.png b/.github/Example_3.png new file mode 100644 index 0000000..9b89cdc Binary files /dev/null and b/.github/Example_3.png differ diff --git a/.github/Example_4.png b/.github/Example_4.png new file mode 100644 index 0000000..14b1b80 Binary files /dev/null and b/.github/Example_4.png differ diff --git a/.github/Example_5.png b/.github/Example_5.png new file mode 100644 index 0000000..9eb9d77 Binary files /dev/null and b/.github/Example_5.png differ diff --git a/.github/Example_6.png b/.github/Example_6.png new file mode 100644 index 0000000..7ebb45a Binary files /dev/null and b/.github/Example_6.png differ diff --git a/.github/Example_7.png b/.github/Example_7.png new file mode 100644 index 0000000..61f1324 Binary files /dev/null and b/.github/Example_7.png differ diff --git a/.github/workflows/branch-push.yaml b/.github/workflows/branch-push.yaml new file mode 100644 index 0000000..58ac262 --- /dev/null +++ b/.github/workflows/branch-push.yaml @@ -0,0 +1,22 @@ +name: Branch push +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + permissions: + # Required to request the OIDC JWT Token + id-token: write + # Required when GH token is used to authenticate with private repo + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Draft release + uses: KoblerS/release-drafter@v1 + with: + version-prefix: '' diff --git a/.github/workflows/release-published.yaml b/.github/workflows/release-published.yaml new file mode 100644 index 0000000..50129cb --- /dev/null +++ b/.github/workflows/release-published.yaml @@ -0,0 +1,29 @@ +name: Release published +on: + release: + types: + - released + +jobs: + build: + runs-on: ubuntu-latest + permissions: + # Required to request the OIDC JWT Token + id-token: write + # Required when GH token is used to authenticate with private repo + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Update version in package.json + run: | + jq ".version = \"${{ github.event.release.tag_name}}\"" package.json > tmp.json && mv tmp.json package.json + + - name: Commit changes + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@github.com' + git add package.json + git commit -m "Update version to ${{ github.event.release.tag_name}}" + git push \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3f11de --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# https://git-scm.com/docs/gitignore +# https://help.github.com/articles/ignoring-files +# Example .gitignore files: https://github.com/github/gitignore +/bower_components/ +/node_modules/ \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..beca4e9 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,66 @@ +module.exports = function(grunt) { + require("time-grunt")(grunt); + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + eslint: { + options: { + configFile: ".eslintrc.json" + }, + target: ["*.js"] + }, + stylelint: { + simple: { + options: { + configFile: ".stylelintrc" + }, + src: ["*.css"] + } + }, + jsonlint: { + main: { + src: ["package.json", "translations/*.json"], + options: { + reporter: "jshint" + } + } + }, + markdownlint: { + all: { + options: { + config: { + "default": true, + "line-length": false, + "blanks-around-headers": false, + "no-duplicate-header": false, + "no-inline-html": false, + "MD010": false, + "MD001": false, + "MD031": false, + "MD040": false, + "MD002": false, + "MD029": false, + "MD041": false, + "MD032": false, + "MD036": false, + "MD037": false, + "MD009": false, + "MD018": false, + "MD012": false, + "MD026": false, + "MD038": false + } + }, + src: ["README.md", "CHANGELOG.md", "LICENSE.txt"] + } + }, + yamllint: { + all: [".travis.yml"] + } + }); + grunt.loadNpmTasks("grunt-eslint"); + grunt.loadNpmTasks("grunt-stylelint"); + grunt.loadNpmTasks("grunt-jsonlint"); + grunt.loadNpmTasks("grunt-yamllint"); + grunt.loadNpmTasks("grunt-markdownlint"); + grunt.registerTask("default", ["eslint", "stylelint", "jsonlint", "markdownlint", "yamllint"]); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fb2f45a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Simon Kobler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 4675bb6..7bd544c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ MagicMirror² Module to monitor public transport (U-Bahn, Tram, Bus, S-Bahn) in | **Stations names with arrival time** | ![](.github/Example_1.png) | ``` config: { - haltestelle: "Hauptbahnhof", + station: "Hauptbahnhof", maxEntries: 10, // 10 items on screen updateInterval: 60000, // 60 s showIcons: false, // Show transport type icon @@ -37,7 +37,7 @@ config: { ``` config: { - haltestelle: "Karlsplatz", + station: "Karlsplatz", maxEntries: 15, // 10 items on screen updateInterval: 60000, // 60 s showIcons: true, // Show transport type icon @@ -61,7 +61,7 @@ config: { | **Stations names with icons, arrival time and walking time** | ![](.github/Example_4.png) | ``` config: { - haltestelle: "Karlsplatz", + station: "Karlsplatz", maxEntries: 15, // 10 items on screen updateInterval: 60000, // 60 s showIcons: true, // Show transport type icon @@ -86,7 +86,7 @@ config: { | **Stations names with icons, arrival time and disruption marking** | ![](.github/Example_5.png) | ``` config: { - haltestelle: "Harras", + station: "Harras", maxEntries: 10, // 10 items on screen updateInterval: 60000, // 60 s showIcons: true, // Show transport type icon @@ -106,6 +106,7 @@ config: { showInterruptions: true, showInterruptionsDetails: false, countInterruptionsAsItemShown: false +} ``` | | | @@ -113,7 +114,7 @@ config: { | **Stations names with icons, arrival time, disruption marking and disruptions details** (disruption details are not counted as new lines)| ![](.github/Example_6.png) | ``` config: { - haltestelle: "Harras", + station: "Harras", maxEntries: 10, // 10 items on screen updateInterval: 60000, // 60 s showIcons: true, // Show transport type icon @@ -133,6 +134,7 @@ config: { showInterruptions: true, showInterruptionsDetails: true, countInterruptionsAsItemShown: false +} ``` | | | @@ -140,7 +142,7 @@ config: { | **Stations names with icons, arrival time, disruption marking and disruptions details** (disruption details are counted as new lines) | ![](.github/Example_7.png) | ``` config: { - haltestelle: "Harras", + station: "Harras", maxEntries: 10, // 10 items on screen updateInterval: 60000, // 60 s showIcons: true, // Show transport type icon @@ -160,6 +162,7 @@ config: { showInterruptions: true, showInterruptionsDetails: true, countInterruptionsAsItemShown: true +} ``` ## Dependencies @@ -177,7 +180,7 @@ config: { position: "bottom_left", header: "MVG", config: { - haltestelle: "Hauptbahnhof", + station: "Hauptbahnhof", maxEntries: 10, // 10 items on screen updateInterval: 60000, // 60 s showIcons: true, // Show transport type icon @@ -203,13 +206,13 @@ config: { showInterruptionsDetails: false, // show details of interruptions in next line countInterruptionsAsItemShown: false, // count interruptions details lines as a line shown } -}, +} ``` ## Config Options | **Option** | **Description** | | --- | --- | -| `haltestelle` | Station for which you want to display data.
**Default:** `Hauptbahnhof`
**Source:** http://www.mvg-live.de/MvgLive/MvgLive.jsp | +| `station` | Station for which you want to display data.
**Default:** `Hauptbahnhof`
**Source:** https://www.mvg.de/meinhalt.html | | `maxEntries` | Number of items shown in table.
**Default:** `8` | | `updateInterval` | Update interval
**Default:** `60000` | | `ubahn` | Show data for U-Bahn.
**Possible values:** `true` or `false`
**Default:** `true` | diff --git a/mmm-mvg.css b/mmm-mvg.css new file mode 100644 index 0000000..7419bd1 --- /dev/null +++ b/mmm-mvg.css @@ -0,0 +1,154 @@ +/* + * MMM-MVG + * + * By Simon Kobler + * MIT Licensed. + * + */ + +.stationColumn { + padding-left: 10px; + padding-right: 10px; + min-width: 240px; +} + +.mmm-mvg table { +border-spacing: 0 0.3em; +} + +.mmm-mvg tr.normal { + color: white; +} + +.mmm-mvg tr.gray { + color: dimgray; +} + +.mmm-mvg td.tram { + background: url(resources/tram.svg) no-repeat 0 0; + width: 30px; + padding-bottom: 5px; + background-size: 25px; +} + +.mmm-mvg td.sbahn { + background: url(resources/s-bahn.svg) no-repeat 0 0; + width: 30px; + padding-bottom: 5px; + background-size: 25px; +} + +.mmm-mvg td.bus, .mmm-mvg td.regional_bus { + background: url(resources/bus.svg) no-repeat 0 0; + width: 30px; + padding-bottom: 5px; + background-size: 25px; +} + +.mmm-mvg td.ubahn { + background: url(resources/u-bahn.svg) no-repeat 0 0; + width: 30px; + padding-bottom: 5px; + background-size: 25px; +} + +.mmm-mvg td.empty { + color: dimgray; + font-size: 16px; + width: 30px; + padding-bottom: 5px; + background-size: 25px; +} + +.mmm-mvg td.timing { + text-align: right; +} + +.U1 { + --bg: #52822F; +} +.U2 { + --bg: #C2243B; +} +.U3 { + --bg: #EC6726; +} +.U4 { + --bg: #00A984; +} +.U5 { + --bg: #BB7A00; +} +.U6 { + --bg: #0065AD; +} +.U7 { + --bg: #000; +} +.U8 { + --bg: #C2243B; + position: relative; + overflow: hidden; + display: inline-block; + z-index: 0; +} +.U8::after { + background-color: #EC6726; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + clip-path: polygon(100% 0, 0% 100%, 100% 100%); + z-index: -1; + content: ''; +} +.S1 { + --bg: #1B9FC6; +} +.S2 { + --bg: #69A338; +} +.S3 { + --bg: #973083; +} +.S4 { + --bg: #136680; +} +.S5 { + --bg: #136680; +} +.S6 { + --bg: #008D5E; +} +.S7 { + --bg: #883B32; +} +.S8 { + --bg: #2D2B29; +} +.SEV { + --bg: #95368C; +} +.TRAM { + --bg: #E30613; +} +.BUS { + --bg: #00586A; +} + +.transport { + background-color: var(--bg); + color: #ffffff; + padding: 0 clamp(.25rem, calc(.13rem + .32vw), .38rem); + text-align: center; + font-variant-numeric: lining-nums tabular-nums; + font-family: Manrope, sans-serif; + font-size: 16px; + font-style: normal; + font-weight: 700; + line-height: 20px; + height: 20px; + border-radius: 4px; + white-space: nowrap; +} diff --git a/mmm-mvg.js b/mmm-mvg.js new file mode 100755 index 0000000..0aecbae --- /dev/null +++ b/mmm-mvg.js @@ -0,0 +1,351 @@ +/* Timetable for public transport in Munich using MVG API */ + +/* + * Magic Mirror + * Module: MVG + * + * By Simon Kobler + * MIT Licensed + * + */ + +const MS_PER_MINUTE = 60000; +Module.register("mmm-mvg", { + // Default module configuration + defaults: { + headerPrefix: '', // Header prefix + maxEntries: 8, // maximum number of results shown on UI + updateInterval: MS_PER_MINUTE, // update every 60 seconds + station: "Hirschgartenallee", // default departure station + stationId: 0, + stationName: "", + ignoreStations: [], // list of destination to be ignored in the list + lineFiltering: { + active: true, // set this to active if filtering should be used + filterType: "blacklist", // whitelist = only specified lines will be displayed, blacklist = all lines except specified lines will be displayed + lineNumbers: ["U1, U3, X50"], // lines that should be on the white-/blacklist + }, + 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: false, + transportTypesToShow: { + ubahn: true, + sbahn: true, + regional_bus: true, + bus: true, + tram: true, + }, + showInterruptions: true, + showInterruptionsDetails: true, + countInterruptionsAsItemShown: false, + }, + + getStyles: function () { + return ["mmm-mvg.css"]; + }, + + // Load translations files + getTranslations: function () { + return { + en: "translations/en.json", + de: "translations/de.json", + }; + }, + + start: function () { + this.resultData = []; + this.interruptionData = null; + Log.info( + "Starting module: " + this.name + ", identifier: " + this.identifier + ); + if (this.config.station !== "") { + 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.station === "") { + wrapperTable.className = "dimmed light small"; + wrapperTable.innerHTML = "Please set value for 'station'."; + return wrapperTable; + } + + if (Object.keys(this.resultData).length === 0) { + 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.stationId]; + return wrapperTable; + }, + + getHtml: function (jsonObject) { + let htmlText = ""; + + let visibleLines = 0; + const interruptions = new Set(); + + for (let i = 0; i < jsonObject.length; i++) { + if (visibleLines >= this.config.maxEntries) { + break; + } + // get one item from api result + const apiResultItem = jsonObject[i]; + // get transport type + const transportType = apiResultItem.transportType.toLocaleLowerCase(); + + // check if we should show data of this transport type + if ( + !this.config.transportTypesToShow[transportType] || + this.config.ignoreStations.includes(apiResultItem.destination) || + this.checkToIgnoreOrIncludeLine(apiResultItem.label) + ) { + continue; + } + + if ( + this.config.showInterruptions && + this.isLineAffected(apiResultItem.label) + ) { + htmlText += ""; + } else { + htmlText += ""; + } + // check if user want's icons + htmlText += this.showIcons(apiResultItem.transportType, this.config.showIcons); + // check if user want's to see transportation badge + htmlText += this.getTransportationBadge( + apiResultItem.transportType, + apiResultItem.label + ); + + // add last station aka direction + htmlText += + "" + apiResultItem.destination + ""; + // check if user want's to see departure time + htmlText += this.showDepartureTime(apiResultItem.plannedDepartureTime); + // check if user want's to see walking time + htmlText += this.showWalkingTime(apiResultItem.plannedDepartureTime); + htmlText += ""; + if ( + this.config.showInterruptionsDetails && + this.isLineAffected(apiResultItem.label) + ) { + let interruption = this.getInterruptionsDetails(apiResultItem.label); + if (!interruptions.has(interruption)) { + interruptions.add(interruption); + htmlText += + "" + + interruption + + ""; + if (this.config.countInterruptionsAsItemShown) { + visibleLines++; + } + } + } + visibleLines++; + } + return htmlText; + }, + + checkToIgnoreOrIncludeLine: function (lineName) { + return ( + this.config.lineFiltering !== undefined && + this.config.lineFiltering.active && + (this.config.lineFiltering.filterType.localeCompare("whitelist") === 0 + ? !this.checkLineNumbersIncludes(lineName) + : this.checkLineNumbersIncludes(lineName)) + ); + }, + + checkLineNumbersIncludes: function (lineName) { + return this.config.lineFiltering.lineNumbers.includes(lineName); + }, + + isLineAffected: function (lineName) { + if (this.interruptionData != undefined) { + for ( + let i = 0; + i < this.interruptionData.length; + i++ + ) { + const line = this.interruptionData[i].lines.find(line => line.label === lineName); + if (line) { + return true; + } + } + } + return false; + }, + + getInterruptionsDetails: function (lineName) { + for (let i = 0; i < this.interruptionData.length; i++) { + if (this.interruptionData[i].lines != null) { + for ( + let j = 0; + j < this.interruptionData[i].lines.length; + j++ + ) { + if ( + this.interruptionData[i].lines[j].label === + lineName + ) { + const validFrom = new Date(this.interruptionData[i].validFrom).toLocaleDateString(undefined, { day: '2-digit', month: '2-digit' }); + let validTo = ''; + if (this.interruptionData[i].validTo) { + validTo = `- ${new Date(this.interruptionData[i].validTo).toLocaleDateString(undefined, { day: '2-digit', month: '2-digit' })}`; + } + return `(${validFrom}${validTo}) - ${this.interruptionData[i].title}`; + } + } + } + } + return ""; + }, + + showIcons: function (product, showIcons) { + let icons = ""; + if (showIcons) { + icons = ""; + } + return icons; + }, + + getTransportationBadge: function (product, label) { + let badge = ""; + switch (product) { + case "UBAHN": + badge = `${label}`; + break; + case "SBAHN": + badge = `${label}`; + break; + case "REGIONAL_BUS": + badge = `${label}`; + break; + case "BUS": + badge = `${label}`; + break; + case "TRAM": + badge = `${label}`; + break; + default: + badge = ""; + } + return badge; + }, + + 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); + // 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.station !== "" || this.config.stationName !== "") { + return ( + this.config.headerPrefix + + " Munich: " + + (this.config.stationName === "" + ? this.config.station + : this.config.stationName) + ); + } + return ""; + }, + socketNotificationReceived: function (notification, payload) { + switch (notification) { + case "UPDATE_DEPARTURE_INFO": + this.resultData[payload.stationId] = this.getHtml(payload.transport); + break; + + case "UPDATE_STATION": + if (this.config.station === payload.station) { + this.config.stationId = payload.stationId; + this.config.stationName = payload.stationName; + } + this.getHeader(); + this.getData(); + break; + + case "UPDATE_INTERRUPTION_DATA": + this.interruptionData = payload; + break; + + default: + Log.error(); + } + this.updateDom(); + }, +}); diff --git a/node_helper.js b/node_helper.js new file mode 100755 index 0000000..fbd72e2 --- /dev/null +++ b/node_helper.js @@ -0,0 +1,129 @@ +/* + * Magic Mirror + * Node Helper: mmm-mvg + * + * By Simon Kobler + * MIT Licensed + * + */ + +const NodeHelper = require("node_helper"); +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", + "X-Requested-With": "XMLHttpRequest", + "X-MVG-Authorization-Key": "5af1beca494712ed38d313714d4caff6", + Referer: "https://www.mvg.de/dienste/abfahrtszeiten.html", + "Accept-Encoding": "gzip", + "Accept-Language": "en-US,en;q=0.9,de;q=0.8", +}; +const fibAPI = "https://www.mvg.de/api/bgw-pt/v3"; + +module.exports = NodeHelper.create({ + socketNotificationReceived: function (notification, payload) { + const self = this; + switch (notification) { + case "GET_STATION_INFO": + console.log("GET_STATION_INFO", payload); + self.getStationInfo(payload); + self.getInterruptionsInfo(); + break; + case "GET_DEPARTURE_DATA": + self.getDepartureInfo(payload); + break; + default: + console.error("Switch item {} is missing", notification); + } + }, + + getDepartureInfo: function (payload) { + const self = this; + const args = { + globalId: payload.stationId, + limit: payload.maxEntries || 10, + offsetInMinutes: 0, + }; + + const query = new URLSearchParams(args).toString(); + fetch(`${fibAPI}/departures?${query}`, { + method: "GET", + headers: globals, + }) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok " + response.statusText); + } + return response.json(); + }) + .then((data) => { + payload.transport = data; + self.sendSocketNotification("UPDATE_DEPARTURE_INFO", payload); + }) + .catch((error) => { + console.error("Error while reading departure info", error); + self.sendSocketNotification( + "ERROR", + "Error while reading data: " + error.message + ); + }); + }, + + getStationInfo(payload) { + const self = this; + + const args = { + query: payload.station + } + const query = new URLSearchParams(args).toString(); + + fetch(`${fibAPI}/locations?${query}`, { + method: "GET", + headers: globals, + }) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok " + response.statusText); + } + return response.json(); + }) + .then((data) => { + if (data?.[0]?.globalId === undefined) { + self.sendSocketNotification("ERROR_NO_STATION", ""); + } else { + payload.stationId = data[0].globalId; + payload.stationName = data[0].name; + self.sendSocketNotification("UPDATE_STATION", payload); + } + }) + .catch((error) => { + console.error("Error while reading station data", error); + self.sendSocketNotification( + "ERROR", + "Error while reading data: " + error.message + ); + }); + }, + + getInterruptionsInfo: function () { + const self = this; + + fetch(`${fibAPI}/messages`, { + method: "GET", + headers: globals, + }) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok " + response.statusText); + } + return response.json(); + }) + .then((data) => { + self.sendSocketNotification("UPDATE_INTERRUPTION_DATA", data); + }) + .catch((error) => { + console.error("Error while reading interruptions data", error); + }); + }, +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d9ccd53 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,43 @@ +{ + "name": "MMM-mvg", + "version": "1.0.8", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "MMM-mvg", + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "urlencode": "~2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/urlencode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/urlencode/-/urlencode-2.0.0.tgz", + "integrity": "sha512-K4+koEq4II9FqKKdLyMwfVFiWvTLJsdsIihXCprumjlOwpviO44E4hAhLYBLb6CEVTZh9hXXMTQHIT+Hwv5BPw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "~0.6.3" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..072e367 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "mmm-mvg", + "version": "1.0.0", + "description": "", + "main": "mmm-mvg.js", + "author": "Simon Kobler", + "license": "MIT", + "homepage": "https://github.com/KoblerS/MMM-MVG", + "keywords": [ + "magic mirror", + "public transport", + "mvv", + "mvg" + ], + "repository": { + "type": "git", + "url": "https://github.com/KoblerS/MMM-MVG" + }, + "contributors": [], + "dependencies": { + "urlencode": "~2.0.0" + } +} diff --git a/resources/bus.svg b/resources/bus.svg new file mode 100644 index 0000000..88ef4c8 --- /dev/null +++ b/resources/bus.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/resources/s-bahn.svg b/resources/s-bahn.svg new file mode 100644 index 0000000..aa1ba3e --- /dev/null +++ b/resources/s-bahn.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/resources/sbahn.svg b/resources/sbahn.svg new file mode 100644 index 0000000..aa1ba3e --- /dev/null +++ b/resources/sbahn.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/resources/tram.svg b/resources/tram.svg new file mode 100644 index 0000000..ad7f01d --- /dev/null +++ b/resources/tram.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/u-bahn.svg b/resources/u-bahn.svg new file mode 100644 index 0000000..b67ef17 --- /dev/null +++ b/resources/u-bahn.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/translations/de.json b/translations/de.json new file mode 100644 index 0000000..2db7acc --- /dev/null +++ b/translations/de.json @@ -0,0 +1,7 @@ +{ + "MIN": "Min.", + "IN": "in", + "JETZT": "Jetzt", + "LOADING": "Lädt ...", + "NO_STATION": "Der Name der station ist nicht korrekt, bitte überprüfe diesen auf mvg.de" +} diff --git a/translations/en.json b/translations/en.json new file mode 100644 index 0000000..497a5ab --- /dev/null +++ b/translations/en.json @@ -0,0 +1,7 @@ +{ + "MIN": "min", + "IN": "in", + "JETZT": "now", + "LOADING": "Loading ...", + "NO_STATION": "The name of the station is not correct, please check on mvg.de" +}