From 355f7de59f8668969d89de53ecaf14d58f58de3f Mon Sep 17 00:00:00 2001 From: Jannick Fahlbusch Date: Thu, 19 Oct 2017 18:24:26 +0200 Subject: [PATCH] Add the ability to specify Gas Stations to display --- MMM-Fuel.js | 4 +- README.md | 1 + apis/tankerkoenig.js | 128 ++++++++++++++++++++++++++++++--------- apis/utils/Coordinate.js | 23 +++++++ 4 files changed, 127 insertions(+), 29 deletions(-) diff --git a/MMM-Fuel.js b/MMM-Fuel.js index 472d3ce..155c25f 100644 --- a/MMM-Fuel.js +++ b/MMM-Fuel.js @@ -69,6 +69,7 @@ Module.register('MMM-Fuel', { * @property {int} updateInterval - Speed of update. * @property {string} provider - API provider of the data. * @property {boolean} toFixed - Flag to show price with only 2 decimals. + * @property {string[]} ids - List of gas stations to display */ defaults: { radius: 5, @@ -89,7 +90,8 @@ Module.register('MMM-Fuel', { rotateInterval: 60 * 1000, // every minute updateInterval: 15 * 60 * 1000, // every 15 minutes provider: 'tankerkoenig', - toFixed: false + toFixed: false, + ids: [] }, /** diff --git a/README.md b/README.md index 535a486..4e88778 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Gas Station Price Module for MagicMirror2 | `rotate` | `true` | Boolean to enable/disable rotation between sort by price and distance. | | `rotateInterval` | `60000` (1 min) | How fast the sorting should be switched between byPrice and byDistance. | | `updateInterval` | `900000` (15 mins) | How often should the data be fetched. | +| `ids` | `[]` | Only display the specified Gas Stations. Works only with TankerKoenig. | ### tankerkoenig (Germany only) diff --git a/apis/tankerkoenig.js b/apis/tankerkoenig.js index d71f2c6..55c35ec 100644 --- a/apis/tankerkoenig.js +++ b/apis/tankerkoenig.js @@ -12,12 +12,14 @@ * @see https://www.npmjs.com/package/request */ const request = require('request'); +const Coordinate = require('./utils/Coordinate.js'); /** * @module apis/tankerkoenig * @description Queries data from tankerkoenig.de * * @requires external:request + * @requires module:Coordinate * * @param {Object} config - Configuration. * @param {number} config.lat - Latitude of Coordinate. @@ -31,12 +33,29 @@ const request = require('request'); */ module.exports = (config) => { /** @member {string} baseUrl - API url */ - const baseUrl = 'https://creativecommons.tankerkoenig.de/json/list.php'; + const baseUrl = 'https://creativecommons.tankerkoenig.de/json'; - /** @member {Object} options - API url combined with config options. */ - const options = { - url: `${baseUrl}?lat=${config.lat}&lng=${config.lng}&rad=${config.radius}&type=all&apikey=${ - config.api_key}&sort=dist` + /** + * @function generateOptions + * @description Helper function to generate the options. + * + * @param {string} gasStationId - Optional gas station ID + * @returns {Object.} Options-object + */ + const generateOptions = (gasStationId) => { + let url = baseUrl; + + if (config.ids && gasStationId && config.ids.length > 0) { + url += `/detail.php?id=${gasStationId}`; + } else { + url += `/list.php?lat=${config.lat}&lng=${config.lng}&rad=${config.radius}&type=all&sort=dist`; + } + + url += `&apikey=${config.api_key}`; + + return { + url + }; }; /** @@ -95,6 +114,64 @@ module.exports = (config) => { /* eslint-enable no-param-reassign */ }; + /** + * @function requestPrice + * @description Function to fetch the details for a given station. + * + * @param {string} gasStationId - ID of the gas station + * + * @returns {Promise} Data or error message + */ + const requestPrice = gasStationId => new Promise((resolve, reject) => { + request(generateOptions(gasStationId), (error, response, body) => { + if (response.statusCode === 200) { + const parsedBody = JSON.parse(body); + + if (parsedBody.ok) { + parsedBody.station.dist = Coordinate.getDistance({ + lat: config.lat, + lng: config.lng + }, { + lat: parsedBody.station.lat, + lng: parsedBody.station.lng + }); + resolve(parsedBody.station); + } else { + reject(`Error getting station details ${parsedBody.status}`); + } + } else { + reject(`Error getting fuel data ${response.statusCode}`); + } + }); + }); + + /** + * @function parseStationList + * @description Helper function to pars the list of gas stations. + * + * @param {Object[]} stationList - List of gas stations + * @param {getDataCallback} callback - Callback that handles the API data. + * + * @returns {Promise} Data or error message + */ + const parseStationList = (stationList, callback) => { + const stations = stationList.filter(filterStations); + + stations.forEach(normalizeStations); + + const price = stations.slice(0); + price.sort(sortByPrice); + + + callback(null, { + types: ['diesel', 'e5', 'e10'], + unit: 'km', + currency: 'EUR', + byPrice: price, + byDistance: stations + }); + }; + return { /** * @callback getDataCallback @@ -111,31 +188,26 @@ module.exports = (config) => { * @param {getDataCallback} callback - Callback that handles the API data. */ getData(callback) { - request(options, (error, response, body) => { - if (response.statusCode === 200) { - const parsedBody = JSON.parse(body); - if (parsedBody.ok) { - const stations = parsedBody.stations.filter(filterStations); - - stations.forEach(normalizeStations); - - const price = stations.slice(0); - price.sort(sortByPrice); - - callback(null, { - types: ['diesel', 'e5', 'e10'], - unit: 'km', - currency: 'EUR', - byPrice: price, - byDistance: stations - }); + if (config.ids && config.ids.length > 0) { + Promise + .all(config.ids.map(requestPrice)) + .then((stationList) => { + parseStationList(stationList, callback); + }, callback); + } else { + request(generateOptions(), (error, response, body) => { + if (response.statusCode === 200) { + const parsedBody = JSON.parse(body); + if (parsedBody.ok) { + parseStationList(parsedBody.stations, callback); + } else { + callback('Error no fuel data'); + } } else { - callback('Error no fuel data'); + callback(`Error getting fuel data ${response.statusCode}`); } - } else { - callback(`Error getting fuel data ${response.statusCode}`); - } - }); + }); + } } }; }; diff --git a/apis/utils/Coordinate.js b/apis/utils/Coordinate.js index 0a8bc12..a93ee89 100644 --- a/apis/utils/Coordinate.js +++ b/apis/utils/Coordinate.js @@ -90,5 +90,28 @@ module.exports = { const λ2 = λ1 + Math.atan2(y, x); return { lat: rad2deg(φ2), lng: ((rad2deg(λ2) + 540) % 360) - 180 }; + }, + + /** + * @function getDistance + * @description Calculates the distance between two points. + * + * @param {Object.} - Start point + * @param {Object.} - Target point + * @returns {number} - Distance in kilometers + */ + getDistance(pos1, pos2) { + // Algorithm taken from https://stackoverflow.com/a/27943 + /* eslint-disable no-mixed-operators */ + + const dLat = deg2rad(pos2.lat - pos1.lat); + const dLon = deg2rad(pos2.lng - pos1.lng); + + const distance = Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(deg2rad(pos1.lat)) * Math.cos(deg2rad(pos2.lat)) * + Math.sin(dLon / 2) * Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(distance), Math.sqrt(1 - distance)); + + return earth * c / 1000; } };