MMM-LocalTransport icon indicating copy to clipboard operation
MMM-LocalTransport copied to clipboard

BugFix for 'Loading...' issue

Open Stephan3555 opened this issue 5 years ago • 1 comments

Hi guys, stumbled over the "Loading..." issue by myself. A short debug session revealed that if the distance is too short, google suggests 'walking'. Unfortunately the google API dont specify 'departure_time' and 'arrival_time' for 'walking'. Hence the code run in an undefined parameter and quits.

I fixed this issue (in a really dirty way) to get it going for me. If you run in the same mistake just replace the MMM-LocalTransport.js with the following code:

/* global Module */
/* Magic Mirror
 * Module: MMM-LocalTransport
 *
 * By Christopher Fenner https://github.com/CFenner
 * style options by Lasse Wollatz
 * MIT Licensed.
 */
Module.register('MMM-LocalTransport', {
  defaults: {
    maximumEntries: 3,
    displayStationLength: 0,
    displayWalkType: 'short',
    displayArrival: true,
    maxWalkTime: 10,
    fade: true,
    fadePoint: 0.1,
    showColor: true,
    maxModuleWidth: 0,
    animationSpeed: 1,
    updateInterval: 5,
    language: config.language,
    units: config.units,
    timeFormat: config.timeFormat,
    mode: 'transit',
    traffic_model: 'best_guess',
    departure_time: 'now',
    alternatives: true,
    apiBase: 'https://maps.googleapis.com/',
    apiEndpoint: 'maps/api/directions/json',
    debug: false
  },
  start: function() {
    Log.info('Starting module: ' + this.name);
    this.loaded = false;
    this.url = this.config.apiBase + this.config.apiEndpoint + this.getParams();
    var d = new Date();
    this.lastupdate = d.getTime() - 2 * this.config.updateInterval * 60 * 1000;
    this.update();
    // refresh every 0.25 minutes
    setInterval(
        this.update.bind(this),
        15 * 1000);
  },
  update: function() {
    //updateDOM
    var dn = new Date();
    if (dn.getTime() - this.lastupdate >= this.config.updateInterval * 60 * 1000){
        //perform main update
        //request routes from Google

	Log.info(this.config.apiBase + this.config.apiEndpoint + this.getParams());

        this.sendSocketNotification(
            'LOCAL_TRANSPORT_REQUEST', {
                id: this.identifier,
                url: this.config.apiBase + this.config.apiEndpoint + this.getParams()
            }
        );
        if (this.config.debug){
          this.sendNotification("SHOW_ALERT", { timer: 3000, title: "LOCAL TRANSPORT", message: "special update"});
        }
        this.lastupdate = dn.getTime();
    }else{
        //perform minor update
        //only update time
        if (this.config.debug){
          this.sendNotification("SHOW_ALERT", {timer: 3000, title: "LOCAL TRANSPORT", message: "normal update"});
        }
        this.loaded = true;
        this.updateDom(); //this.updateDom(this.config.animationSpeed * 1000)
    }
  },
  getParams: function() {
    var params = '?';
    params += 'mode=' + this.config.mode;
    params += '&origin=' + this.config.origin;
    params += '&destination=' + this.config.destination;
    params += '&key=' + this.config.api_key;
    params += '&traffic_model=' + this.config.traffic_model;
    params += '&departure_time=now';
    params += '&alternatives=true';
    return params;
  },
  renderLeg: function(wrapper, leg){
    /* renderLeg
     * creates HTML element for one leg of a route
     */

    /*
    * If Distance is too short, Google suggests walking. Therefore 'deparure' and 'arrival'
    * are not set by the google API. Have to calculate by myself
    */
    if(leg.departure_time === undefined){
	var depature = new Date();
    	var arrival = new Date() + leg.duration.value;
    } else {
	var depature = leg.departure_time.value * 1000;
	var arrival = leg.arrival_time.value * 1000;
    }

    //var depadd = leg.start_address;
    var span = document.createElement("div");
    span.className = "small bright";
    span.innerHTML = moment(depature).locale(this.config.language).fromNow();
    // span.innerHTML += "from " + depadd;
    if (this.config.displayArrival && this.config.timeFormat === 24){
        span.innerHTML += " ("+this.translate("ARRIVAL")+": " + moment(arrival).format("H:mm") + ")";
    }else if(this.config.displayArrival){
        span.innerHTML += " ("+this.translate("ARRIVAL")+": " + moment(arrival).format("h:mm") + ")";
    }
    // span.innerHTML += this.translate('TRAVEL_TIME') + ": ";
    // span.innerHTML += moment.duration(moment(arrival).diff(depature, 'minutes'), 'minutes').humanize();
    wrapper.appendChild(span);
  },
  renderStep: function(wrapper, step){
    /* 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 >= (this.config.maxWalkTime*60)){
            /*if time of walking is longer than
             *specified, mark this route to be skipped*/
            wrapper.innerHTML = "too far";
        }else if(this.config.displayWalkType != 'none'){
            /*if walking and walking times should be
             *specified, add symbol and time*/
            var img = document.createElement("img");
            if(this.config.showColor){
                img.className = "symbol";
            }else{
                img.className = "symbol bw";
            }
            img.src = "http://maps.gstatic.com/mapfiles/transit/iw2/6/walk.png";
            //img.src = "/localtransport/walk.png"; //needs to be saved in localtransport/public/walk.png
            wrapper.appendChild(img)
            var span = document.createElement("span");
            span.innerHTML = moment.duration(duration, 'seconds').locale(this.config.language).humanize();
            if(this.config.displayWalkType === 'short'){
                span.innerHTML = span.innerHTML.replace(this.translate("MINUTE_PL"),this.translate("MINUTE_PS"));
                span.innerHTML = span.innerHTML.replace(this.translate("MINUTE_SL"),this.translate("MINUTE_SS"));
                span.innerHTML = span.innerHTML.replace(this.translate("SECOND_PL"),this.translate("SECOND_PS"));
            }
            span.className = "xsmall dimmed";
            wrapper.appendChild(span);
        }else{
            /*skip walking*/
            return;
        }
    }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(this.config.showColor){
                img.className = "symbol";
            }else{
                img.className = "symbol bw";
            }
            /* get symbol online*/
            img.src = details.line.vehicle.local_icon || ("http:" + 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 (this.config.displayStationLength > 0){
                /* add departure stop (shortened)*/
                span.innerHTML += " ("+this.translate("FROM")+" " + this.shorten(details.departure_stop.name, this.config.displayStationLength) + ")";
            }else if (this.config.displayStationLength === 0){
                /* add departure stop*/
                span.innerHTML += " ("+this.translate("FROM")+" " + details.departure_stop.name + ")";
            }
            if (this.config.debug){
                /* add vehicle type for debug*/
                span.innerHTML += " [" + details.line.vehicle.name +"]";
            }
            span.className = "xsmall dimmed";
            wrapper.appendChild(span);
        }
    }
  },
  socketNotificationReceived: function(notification, payload) {
    if (notification === 'LOCAL_TRANSPORT_RESPONSE' && payload.id === this.identifier) {
        Log.info('received' + notification);
        if(payload.data && payload.data.status === "OK"){
            this.info = payload.data;
            this.loaded = true;
            this.updateDom(this.config.animationSpeed * 1000);
        }
    }
  },
  getStyles: function() {
    return ["localtransport.css"];
  },
  getScripts: function() {
        return ["moment.js"];
  },
  getTranslations: function() {
    return {
        de: "i18n/de.json",
        en: "i18n/en.json",
        sv: "i18n/sv.json",
        fr: "i18n/fr.json"
    };
  },
  getDom: function() {
    /* main function creating HTML code to display*/
    var wrapper = document.createElement("div");
    if (!this.loaded) {
        /*if not loaded, display message*/
        wrapper.innerHTML = this.translate("LOADING_CONNECTIONS");
        wrapper.className = "small dimmed";
    }else{
        /*create an unsorted list with each
         *route alternative being a new list item*/
        //var udt = document.createElement("div");
        //udt.innerHTML = moment().format("HH:mm:ss") + " (" +  this.lastupdate + ")";
        //wrapper.appendChild(udt);
        var ul = document.createElement("ul");
        var Nrs = 0; //number of routes
        var routeArray = []; //array of all alternatives for later sorting
        for(var routeKey in this.info.routes) {
            /*each route describes a way to get from A to Z*/
            //if(Nrs >= this.config.maxAlternatives){
            //  break;
            //}
	    Log.info("Routkey: " + routeKey);
            var route = this.info.routes[routeKey];
            var li = document.createElement("li");
            li.className = "small";
            var arrival = 0;
            if (this.config.maxModuleWidth > 0){
              li.style.width = this.config.maxModuleWidth + "px";
            }
            for(var legKey in route.legs) {
                var leg = route.legs[legKey];
               
                /*
    		* If Distance is too short, Google suggests walking. Therefore 'arrival' 
		* is not set by the google API. Have to calculate by myself
   		*/

		if (leg.arrival_time === undefined){
			arrival = new Date() + leg.duration.value;
		} else {
			arrival = leg.arrival_time.value;
		}
//		arrival = leg.arrival_time.value;
                var tmpwrapper = document.createElement("text");
                for(var stepKey in leg.steps) {
                    /*each leg consists of several steps
                     *e.g. (1) walk from A to B, then
                           (2) take the bus from B to C and then
                           (3) walk from C to Z*/
                    var step = leg.steps[stepKey];
                    this.renderStep(tmpwrapper, step);
                    if (tmpwrapper.innerHTML === "too far"){
                        //walking distance was too long -> skip this option
                        break;
                    }
                }
                if (tmpwrapper.innerHTML === "too far"){
                    //walking distance was too long -> skip this option
                    li.innerHTML = "too far";
                    break;
                }
                this.renderLeg(li, leg);
                li.appendChild(tmpwrapper);
            }
            if (li.innerHTML !== "too far"){
                routeArray.push({"arrival":arrival,"html":li});
                Nrs += 1;
            }
        }

        /*sort the different alternative routes by arrival time*/
        routeArray.sort(function(a, b) {
            return parseFloat(a.arrival) - parseFloat(b.arrival);
        });
        /*only show the first few options as specified by "maximumEntries"*/
        routeArray = routeArray.slice(0, this.config.maximumEntries);

        /*create fade effect and append list items to the list*/
        var e = 0;
        Nrs = routeArray.length;
        for(var dataKey in routeArray) {
            var routeData = routeArray[dataKey];
            var routeHtml = routeData.html;
            // Create fade effect.
            if (this.config.fade && this.config.fadePoint < 1) {
                if (this.config.fadePoint < 0) {
                    this.config.fadePoint = 0;
                }
                var startingPoint = Nrs * this.config.fadePoint;
                var steps = Nrs - startingPoint;
                if (e >= startingPoint) {
                    var currentStep = e - startingPoint;
                    routeHtml.style.opacity = 1 - (1 / steps * currentStep);
                }
            }
            ul.appendChild(routeHtml);
            e += 1;
        }
        wrapper.appendChild(ul);
    }
    return wrapper;
  },
  shorten: function(string, maxLength) {
    /*shorten
     *shortens a string to the number of characters specified*/
    if (string.length > maxLength) {
        return string.slice(0,maxLength) + "&hellip;";
    }
    return string;
  }

});

I am not doing a pull request because I think the owner abandoned the project already.

Greetings, Stephan

Stephan3555 avatar Jan 14 '19 17:01 Stephan3555

Hi Stephan, If you can fix this in my version (https://github.com/GHLasse/MagicMirror-LocalTransport-Module) I am happy to pull it there.

GHLasse avatar Feb 24 '19 12:02 GHLasse