javascriptmagic-mirror

Modifying an API request from one source to another - can't pull the right information out what am I doing wrong?


Its been two decades since I took a coding class (and I did not use it after I graduated).

I am setting up a MagicMirror dashboard and found a travel times website (www.traveltime.com) that has an API that should work without having to put a credit card down (like many of the others). There are modules on MagicMirror but none using this site.

Is this possible to do??

The API results are:

{"results":[{"search_id":"Matrix","locations":[{"id":"41.87690559224833,-87.63761648086727","properties":[{"travel_time":917}]}],"unreachable":[]}]}

The code I am trying to use is the following:

 Module.register('MMM-Traffic', {
  defaults: {
    interval: 300000,
    showSymbol: true,
    firstLine: 'Current duration is {travel_time} mins',
    loadingText: 'Loading...',
    language: config.language,
    mode: 'driving',
    days: [0, 1, 2, 3, 4, 5, 6],
    hoursStart: '00:00',
    hoursEnd: '23:59'
  },

  start: function () {
    console.log('Starting module: ' + this.name);
    this.loading = true;
    this.internalHidden = false;
    this.firstResume = true;
    this.errorMessage = undefined;
    this.errorDescription = undefined;
    this.updateCommute = this.updateCommute.bind(this);
    this.getCommute = this.getCommute.bind(this);
    this.getDom = this.getDom.bind(this);
    if ([this.config.originCoordsLAT, this.config.originCoordsLNG, this.config.destinationCoords, this.config.accessToken].includes(undefined)) {
      this.errorMessage = 'Config error';
      this.errorDescription = 'You must set originCoordsLAT, originCoordsLNG, destinationCoords, and accessToken in your config';
      this.updateDom();
    } else {
      this.updateCommute();
    }
  },
 
  updateCommute: function () {
    var today = new Date()
    var date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
    var time = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
    var dateTime = date+'T'+time+'-06:00';
    let mode = this.config.mode == 'driving' ? 'driving' : this.config.mode;
    this.url = encodeURI(`https://api.traveltimeapp.com/v4/time-filter?type=${mode}&departure_time=${dateTime}&search_lat=${this.config.originCoordsLAT}&search_lng=${this.config.originCoordsLNG}&locations=${this.config.destinationCoords}&app_id=TEXT&api_key=TEXT`);
    // only run getDom once at the start of a hidden period to remove the module from the screen, then just wait until time to unhide to run again
    if (this.shouldHide() && !this.internalHidden) {
      console.log('Hiding MMM-Traffic due to config options: days, hoursStart, hoursEnd');
      this.internalHidden = true;
      this.updateDom();
    } else if (!this.shouldHide()) {
      this.internalHidden = false;
      this.getCommute(this.url);
    }
    // no network requests are made when the module is hidden, so check every 30 seconds during hidden
    // period to see if it's time to unhide yet
    setTimeout(this.updateCommute, this.internalHidden ? 3000 : this.config.interval);
  },

  
  getCommute: function (api_url) {
    var self = this;
    fetch(api_url)
      .then(self.checkStatus)
      .then(json => {
        self.travel_time = ; //this is where whatever I put here, it won't pull the travel_time information. I get undefined, etc. If I put a text or number, it does show that
        self.errorMessage = self.errorDescription = undefined;
        self.loading = false;
        self.updateDom();
      })
      .catch(e => {
        self.errorMessage = payload.error.message;
        self.errorDescription = payload.error.description;
        self.loading = false;
        self.updateDom();
      });

  },

  checkStatus: function (res) {
    if (res.ok) {
      return res.json();
    } else {
      return res.json().then(json => {
        throw new MMMTrafficError(`API Error - ${json.code}`, json.message);
      });
    }
  },

  getStyles: function () {
    return ['traffic.css', 'font-awesome.css'];
  },

  getScripts: function () {
    return ['moment.js'];
  },

  getDom: function () {
    var wrapper = document.createElement("div");

    // hide when desired (called once on first update during hidden period)
    if (this.internalHidden) return wrapper;

    // base divs
    var firstLineDiv = document.createElement('div');
    firstLineDiv.className = 'bright medium mmmtraffic-firstline';
    var secondLineDiv = document.createElement('div');
    secondLineDiv.className = 'normal small mmmtraffic-secondline';

    // display any errors
    if (this.errorMessage) {
      firstLineDiv.innerHTML = this.errorMessage;
      secondLineDiv.innerHTML = this.errorDescription;
      wrapper.append(firstLineDiv);
      wrapper.append(secondLineDiv);
      return wrapper;
    }

    let symbolString = 'car';
    if (this.config.mode == 'cycling') symbolString = 'bicycle';
    if (this.config.mode == 'walking') symbolString = 'walking';

    // symbol
    if (this.config.showSymbol) {
      var symbol = document.createElement('span');
      symbol.className = `fa fa-${symbolString} symbol`;
      firstLineDiv.appendChild(symbol);
    }

    // first line
    var firstLineText = document.createElement('span');
    firstLineText.innerHTML = this.loading ? this.config.loadingText : this.replaceTokens(this.config.firstLine)
    firstLineDiv.appendChild(firstLineText);
    wrapper.appendChild(firstLineDiv);
    if (this.loading) return wrapper;

    // second line
    if (this.config.secondLine) {
      secondLineDiv.innerHTML = this.replaceTokens(this.config.secondLine);
      wrapper.appendChild(secondLineDiv);
    }

    return wrapper;
  },

  replaceTokens: function (text) {
    return text.replace(/{travel_time}/g, this.travel_time);
  },

  shouldHide: function () {
    let hide = true;
    let now = moment();
    if (this.config.days.includes(now.day()) &&
      moment(this.config.hoursStart, 'HH:mm').isBefore(now) &&
      moment(this.config.hoursEnd, 'HH:mm').isAfter(now)
    ) {
      hide = false;
    }
    return hide;
  },
});

Text from config file:

{
module: "MMM-Traffic",
position: "top_left",
config: {
accessToken: "12345",
originCoordsLAT: "41.95581649395376",
originCoordsLNG: "-87.87083898397289",
destinationCoords: "41.876602828388485_-87.63840104746535",
firstLine: "{travel_time} mins",
}
},

I really just need to get the "travel_time" data point out and then divide it by 60 to get travel time in minutes.

Any help or guidance would be greatly appreciated!

Thank you, Jim


Solution

  • Considering that the API returns exacly as you showed, you can access the key you want by doing this (the question marks are optional chaining)

    self.travel_time = json.results?.[0]?.locations?.[0]?.properties?.[0]?.travel_time;