Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9baa1d0
commit changes from past years - language bug fixes, style changes an…
GHLasse Oct 5, 2017
5e3ec9f
Merge branch 'master' of https://github.com/CFenner/MagicMirror-Local…
GHLasse Oct 5, 2017
a9ddf39
added function to read API key from main config settings - this simpl…
GHLasse Oct 5, 2017
31128c5
Added code to display departure location for walking steps
GHLasse Oct 5, 2017
c47a981
modified less and css to make symbols a bit smaller (to match text fo…
GHLasse Oct 5, 2017
3b60f6c
modified icons to load via https
GHLasse Oct 5, 2017
6ada2d8
added options to display alternative routes
GHLasse Nov 8, 2017
96366ee
simplified some code by adding new functions
GHLasse Nov 8, 2017
5dec810
simplified code by adding some functions
GHLasse Nov 8, 2017
52c3389
simplified code by adding new function
GHLasse Nov 8, 2017
2c5ca06
updated readme
GHLasse Nov 8, 2017
8bd9ba4
display error messages and bug fixes
GHLasse Jan 20, 2018
05fafd3
Merge branch 'master' of https://github.com/CFenner/MagicMirror-Local…
GHLasse Jan 20, 2018
9665935
refactored some varibles and tried to simplify code according to code…
GHLasse Jan 20, 2018
d272007
code simplification in the spirit of codeclimate
GHLasse Jan 20, 2018
5b3872c
code simplification in the spirit of codeclimate
GHLasse Jan 20, 2018
11a5f06
added helper.js to outsource small functions to reduce number of func…
GHLasse Jan 20, 2018
cca9b78
moved renderLef into helper.js and started to look into moving parts …
GHLasse Feb 10, 2018
abdbbba
removed old comments
GHLasse Feb 10, 2018
1971ed4
simplified receiveMain and moved receiveAlternative
GHLasse Feb 11, 2018
32f1a62
placed rendering of fade effect in its own function and simplified ge…
GHLasse Feb 11, 2018
ea2ee38
simplified renderFade
GHLasse Feb 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
672 changes: 415 additions & 257 deletions MMM-LocalTransport.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ Add module configuration to config.js.
},
```

For the header, the following keys can be specified and will be replaced with the according values at runtime:
use `%{orig}` in the header definition for the module and it will be replaces with the origin as defined in the config
use `%{dest}` in the header definition for the module and it will be replaces with the destination as defined in the config/ calendar event
use `%{destX}` in the header definition for the module and it will be replaces with the destination as returned by Google
These options are particularly useful if the destination is changed according to the calendar. The `%{orig}` option may become useful in the future.

|Option|Description|
|---|---|
|`apiKey`|The API key, which can be obtained [here](https://developers.google.com/maps/documentation/directions/).<br><br>This value is **REQUIRED**|
Expand All @@ -49,8 +55,12 @@ Add module configuration to config.js.
|`maximumEntries`|Maximum number of routes to display. Less routes will be shown if less are returned by Google. Routes will be sorted by arrival time. This option should take an integer value between `1` and `5`.<br><br>**Default value:** `3`|
|`updateInterval`|How often does the content need to be fetched? (Minutes) Note that the module refreshes every 15 seconds to always display an accurate estimate when you need to leave.<br><br>**Default value:** `5`|
|`animationSpeed`|Speed of the update animation. (Seconds)<br><br>**Default value:** `1`|
|`getCalendarLocation`|Boolean to specify whether to read calendar notifications send by the default calendar module to display the route to the next event. The event must be less than one day away and contain a location to be considered for this functionality.<br><br>**Default value:** `false`|
|`displayStationLength`|Number of characters of the departure station for each transport mode to display. <br>`0` means display all, <br>`-1` means don't show the departure station<br><br>**Default value:** `0`|
|`displayArrival`|Boolean if the arrival time should be shown in the header for each option.<br><br>**Default value:** `true`|
|`displayAltWalk`|Boolean if a note should be shown with the time it would take to walk instead of using public transport.<br><br>**Default value:** `false`|
|`displayAltCycle`|Boolean if a note should be shown with the time it would take to take a bicycle instead of using public transport.<br><br>**Default value:** `false`|
|`displayAltDrive`|Boolean if a note should be shown with the time it would take to drive instead of using public transport.<br><br>**Default value:** `false`|
|`displayWalkType`|String how detailed the walking segments should be shown. <br> `'none'` means to not display anything,<br> `'short'` means to display the symbol, the time and the short version of the unit or <br> `'long'` means that a symbol, the time and the long string for the unit is displayed. This options is default if an invalid string is supplied.<br><br>**Default value:** `'short'`|
|`maxWalkTime`|Maximum time you are willing to walk between stations in minutes<br><br>**Default value:** `10`|
|`maxModuleWidth`|Maximum width of the module in pixel. Unlimited when set to `0`. This option can be used to make the module shorter in case of very long lines for directions. <br><br>**Default value:** `0`|
Expand Down
194 changes: 194 additions & 0 deletions helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/* small collection of functions that do basic things
* main purpose is to reduce the number of functions per file
*/

function getSymbol(symName,showColor){
var img = document.createElement("img");
if(showColor){
img.className = "symbol";
}else{
img.className = "symbol bw";
}
img.src = "https://maps.gstatic.com/mapfiles/transit/iw2/6/"+symName+".png";
//img.src = "/localtransport/"+symName+".png"; //needs to be saved in localtransport/public/walk.png
return img;
}


function shorten(string, maxLength) {
/*shorten
*shortens a string to the number of characters specified*/
if (string.length > maxLength) {
return string.slice(0,maxLength) + "&hellip;";
}
return string;
}

function shortenAddress(address) {
/*shortenAddress
*shortens a string to the first part of an address
*this assumes that the address string is split by ','
*useful since Google will require a very precise
*definition of an address while the user would usually
*know which city and country they are looking at*/
var temp = address.split(",");
return temp[0];
}

function receiveAlternative(notification, payload, ignoreErrors){
var ans = "unknown";
var errlst = ignoreErrors;
if(payload.data && payload.data.status === "OK"){
Log.log('received ' + notification);
//only interested in duration, first option should be the shortest one
var route = payload.data.routes[0];
var leg = route.legs[0];
ans = leg.duration.value;
}else if (errlst.indexOf(payload.data.status) < 0){
Log.warn('received '+notification+' with status '+payload.data.status);
}else{
Log.info('received '+notification+' with status '+payload.data.status);
}
return ans;
}

function renderLeg(wrapper, leg, arrivalWord, config){
/* renderLeg
* creates HTML element for one leg of a route
*/
var depature = leg.departure_time.value * 1000;
var arrival = leg.arrival_time.value * 1000;
var span = document.createElement("div");
span.className = "small bright";
span.innerHTML = moment(depature).locale(config.language).fromNow();
if (config.displayArrival){
span.innerHTML += " ("+config.test+arrivalWord+": ";
if (config.timeFormat === 24){
span.innerHTML += moment(arrival).format("H:mm");
}else{
span.innerHTML += moment(arrival).format("h:mm");
}
span.innerHTML += ")";
}
wrapper.appendChild(span);
}

function renderDeparture(span, departureStop, fromWord, config){
if (config.displayStationLength > 0){
/* add departure stop (shortened)*/
span.innerHTML += " ("+fromWord+" " + shorten(departureStop, config.displayStationLength) + ")";
}else if (config.displayStationLength === 0){
/* add departure stop*/
span.innerHTML += " ("+fromWord+" " + config._laststop + ")";
}
}

function calcOpacity(Nrs,e,config){
if (config.fadePoint < 0) {
config.fadePoint = 0;
}
if (config.fade && config.fadePoint < 1) {
var startingPoint = Nrs * config.fadePoint;
var steps = Nrs - startingPoint;
if (e >= startingPoint) {
var currentStep = e - startingPoint;
return = 1 - (1 / steps * currentStep);
}
}else{
return 1;
}
}

function renderFade(routeArray,config){
/*create fade effect and append list items to the list*/
var ul = document.createElement("ul");
var e = 0;
var Nrs = routeArray.length;
for(var dataKey in routeArray) {
var routeData = routeArray[dataKey];
var routeHtml = routeData.html;
// Create fade effect.
routeHtml.style.opacity = calcOpacity(Nrs,e,config);
ul.appendChild(routeHtml);
e += 1;
}
return ul
}

//function renderStep(wrapper, step, fromWord, config){
// /* renderStep
// * creates HTML element for one step of a leg
// */
// if(step.travel_mode === "WALKING"){
// /*this step is not public transport but walking*/
// var duration = step.duration.value;
// if (duration >= (config.maxWalkTime*60)){
// /*if time of walking is longer than
// *specified, mark this route to be skipped*/
// wrapper.innerHTML = "too far";
// }else if(config.displayWalkType != 'none'){
// /*if walking and walking times should be
// *specified, add symbol and time*/
// wrapper.appendChild(getSymbol("walk",config.showColor));
// var span = document.createElement("span");
// span.innerHTML = this.convertTime(duration);
// if (config._laststop !== ''){
// /* walking step doesn't have a departure_stop set - maybe something else but can't find the documentation right now.
// so in order to display the departure, we will just save the arrival of any transit step into a global variable and
// display the previous arrival instead of the current departure location. That means we need to reset the global variable
// to not cause interference between different routes and we need to skip the display for the first step if that is a walking
// step (alternatively one could display the departure location specified by the user, but I prefer this option)
// */
// if (config.displayStationLength > 0){
// /* add departure stop (shortened)*/
// span.innerHTML += " ("+fromWord+" " + shorten(config._laststop, config.displayStationLength) + ")";
// }else if (config.displayStationLength === 0){
// /* add departure stop*/
// span.innerHTML += " ("+fromWord+" " + config._laststop + ")";
// }
// }
// span.className = "xsmall dimmed";
// wrapper.appendChild(span);
// }else{
// /*skip walking*/
// return;
// }
// config._laststop = '';
// }else{
// /*if this is a transit step*/
// var details = step.transit_details;
// if(details) {
// /*add symbol of transport vehicle*/
// var img = document.createElement("img");
// if(config.showColor){
// img.className = "symbol";
// }else{
// img.className = "symbol bw";
// }
// /* get symbol online*/
// img.src = details.line.vehicle.local_icon || ("https:" + details.line.vehicle.icon);
// /* can provide own symbols under /localtransport/public/*.png */
// //img.src = "/localtransport/" + details.line.vehicle.name + ".png";
// img.alt = "[" + details.line.vehicle.name +"]";
// wrapper.appendChild(img);
// /*add description*/
// var span = document.createElement("span");
// /* add line name*/
// span.innerHTML = details.line.short_name || details.line.name;
// if (config.displayStationLength > 0){
// /* add departure stop (shortened)*/
// span.innerHTML += " ("+fromWord+" " + shorten(details.departure_stop.name, config.displayStationLength) + ")";
// }else if (config.displayStationLength === 0){
// /* add departure stop*/
// span.innerHTML += " ("+fromWord+" " + details.departure_stop.name + ")";
// }
// if (config.debug){
// /* add vehicle type for debug*/
// span.innerHTML += " [" + details.line.vehicle.name +"]";
// }
// config._laststop = details.arrival_stop.name;
// span.className = "xsmall dimmed";
// wrapper.appendChild(span);
// }
// }
// }
10 changes: 9 additions & 1 deletion i18n/de.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
{
"LOADING_CONNECTIONS": "Lade Verbindungen ...",
"OK": "Ich kenne die Antwort aber ich sage sie dir nicht :-P Dies ist h�chstwahrscheinlich ein bug.",
"NOT_FOUND": "Start- oder Endpunkt konntent nicht gefunden werden.",
"ZERO_RESULTS": "Keine Verbindung gefunden.",
"INVALID_REQUEST": "Es wurde eine ung�ltige Anfrage gemacht.",
"OVER_QUERY_LIMIT": "Sie haben zu viele Anfragen gestellt.",
"REQUEST_DENIED": "Google hat ihre Anfrage abgewiesen.",
"UNKNOWN_ERROR": "Irgendetwas ist falsch gelaufen.",
"ARRIVAL": "Ankunft",
"MINUTE_PL": "Minuten",
"MINUTE_SL": "Minute",
"SECOND_PL": "Sekunden",
"MINUTE_PS": "min",
"MINUTE_SS": "min",
"SECOND_PS": "sek",
"FROM": "von"
"FROM": "von",
"ALT": "Alternativen"
}
10 changes: 9 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
{
"LOADING_CONNECTIONS": "Loading ...",
"OK": "I have a valid answer, but I don't want to show it to you :-P This is most likely a bug.",
"NOT_FOUND": "Either origin or destination couldn't be found.",
"ZERO_RESULTS": "No route found.",
"INVALID_REQUEST": "An invalid request was send.",
"OVER_QUERY_LIMIT": "You send too many requests.",
"REQUEST_DENIED": "Google denied your request.",
"UNKNOWN_ERROR": "Something went wrong. No one knows what.",
"ARRIVAL": "arriving",
"MINUTE_PL": "minutes",
"MINUTE_SL": "minute",
"SECOND_PL": "seconds",
"MINUTE_PS": "min",
"MINUTE_SS": "min",
"SECOND_PS": "sec",
"FROM": "from"
"FROM": "from",
"ALT": "alternatives"
}
3 changes: 2 additions & 1 deletion i18n/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"MINUTE_PS": "min",
"MINUTE_SS": "min",
"SECOND_PS": "s",
"FROM": "från"
"FROM": "från",
"ALT": "alternativ"
}
33 changes: 32 additions & 1 deletion localtransport.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
.MMM-LocalTransport ul{margin:0;padding:0;list-style:none;text-align:left}.MMM-LocalTransport ul li{margin-bottom:.25em}.MMM-LocalTransport ul li img.bw{-webkit-filter:grayscale(100%)}.MMM-LocalTransport ul li img:first-child{margin-left:0}.MMM-LocalTransport ul li img{margin:0 .25em 0 .5em}.MMM-LocalTransport ul li div{margin-left:.5em}
/*.localtransport ul {
margin: 0;
padding: 0;
list-style: none;
text-align: left;
}
.localtransport ul li {
margin-bottom: .25em;
}
.localtransport ul li img:first-child {
margin-left: 0;
}
.localtransport ul li img {
margin: 0 .25em 0 .5em;
max-height: 15px;
max-width: 15px;
height: 15px;
width: 15px;
}
.localtransport ul li div {
margin-left: .5em;
}

.localtransport img.symbol.bw {
-webkit-filter: grayscale(100%);
}*/
.MMM-LocalTransport ul{margin:0;padding:0;list-style:none;text-align:left}
.MMM-LocalTransport ul li{margin-bottom:.25em}
.MMM-LocalTransport ul li img.bw{-webkit-filter:grayscale(100%)}
.MMM-LocalTransport ul li img:first-child{margin-left:0}
.MMM-LocalTransport ul li img{margin:0 .25em 0 .5em; max-height: 15px; max-width: 15px; height: 15px; width: 15px;}
.MMM-LocalTransport ul li div{margin-left:.5em}
/* This beautiful CSS-File has been crafted with LESS (lesscss.org) and compiled by simpLESS (wearekiss.com/simpless) */
4 changes: 4 additions & 0 deletions localtransport.less
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
}
img{
margin: 0 .25em 0 .5em;
max-height: 15px;
max-width: 15px;
height: 15px;
width: 15px;
}
div{
margin-left: .5em;
Expand Down
21 changes: 17 additions & 4 deletions node_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,34 @@ module.exports = NodeHelper.create({
start: function () {
console.log(this.name + ' helper started ...');
},
socketNotificationReceived: function(notification, payload) {
//console.log(notification);
if (notification === 'LOCAL_TRANSPORT_REQUEST') {
makeAPIrequest: function(payload,responseName){
var that = this;
request({
url: payload.url,
method: 'GET'
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
that.sendSocketNotification('LOCAL_TRANSPORT_RESPONSE', {
that.sendSocketNotification(responseName, {
id: payload.id,
data: JSON.parse(body)
});
}
});
},
socketNotificationReceived: function(notification, payload) {
//console.log(notification);

if (notification === 'LOCAL_TRANSPORT_REQUEST') {
this.makeAPIrequest(payload,'LOCAL_TRANSPORT_RESPONSE');
}
if (notification === 'LOCAL_TRANSPORT_WALK_REQUEST') {
this.makeAPIrequest(payload,'LOCAL_TRANSPORT_WALK_RESPONSE');
}
if (notification === 'LOCAL_TRANSPORT_CYCLE_REQUEST') {
this.makeAPIrequest(payload,'LOCAL_TRANSPORT_CYCLE_RESPONSE');
}
if (notification === 'LOCAL_TRANSPORT_DRIVE_REQUEST') {
this.makeAPIrequest(payload,'LOCAL_TRANSPORT_DRIVE_RESPONSE');
}
}
});