UNPKG

hyperbutter-weather

Version:

A plugin to get weather from Yahoo and send it into the Hyper Butter server

177 lines (152 loc) 5.51 kB
'use strict'; const EventEmitter = require('events'); const util = require('util'); const restler = require('restler'); const ENDPOINT = 'https://query.yahooapis.com/v1/public/yql'; const DEFAULT_UPDATE_MINS = 5; const woeIds = []; const cache = {}; let updateTimer = undefined; let lastUpdate = 0; function Weather(config, settings) { EventEmitter.call(this); this.init = () => { this.emit('subscribe', { 'get-forecast': this.getForecast, 'get-update': this.update, }); // if we have some pre-defined places, let's go ahead and fetch the forecast if (config.places) { let foundCount = 0; config.places.forEach((place) => { this.getForecast({ place, watch: true }, (error, forecast) => { if (error) this.emit('error', error); else foundCount++; if (foundCount === config.places.length) this.update(); }); }); } // create a timer to update the weather updateTimer = setInterval(this.update, (config.updateMins || DEFAULT_UPDATE_MINS) * 60000); }; const getWoeidFromName = (name, callback) => { restler.get(ENDPOINT, { query: { q: `select woeid from geo.places(1) where text="${name}"`, format: 'json', }, }) .on('complete', (response) => { const results = response.query.results; if (results === null) { const errorString = `Could not find a place with the name "${name}"`; if (callback) callback (errorString); this.emit('warn', errorString); return; } if (callback) callback(null, results.place.woeid); }); }; // makes something a little easier to consume const createForecastObject = (channel) => { if (channel === null || channel === undefined || channel.item === undefined) return {}; return { location: channel.location, condition: channel.item.condition, forecast: channel.item.forecast, lastUpdate: channel.item.pubDate, units: channel.units, astronomy: channel.astronomy, wind: channel.wind, atmosphere: channel.atmosphere, }; }; /** Gets a forecast with the option to also keep watching it @param args.place {String} - The place you want to get weather on @param args.watch {Boolean} - True if you want to keep receving updates about this place **/ this.getForecast = (args, callback) => { if (args.place === undefined) { if (callback) callback('getForecast missing "place" in object argument'); return; } getWoeidFromName(args.place, (error, woeid) => { if (woeid !== undefined) { // go grab the forecast restler.get(ENDPOINT, { query: { q: `select * from weather.forecast where woeid=${woeid}`, format: 'json', }, }) .on('complete', (response) => { const results = response.query.results; if (results === null) { const errorString = `Could not get a forecast for "${args.place}"`; if (callback) callback (errorString); this.emit('warn', errorString); return; } if (args.watch === true && woeIds.indexOf(woeid) === -1) { woeIds.push(woeid); this.emit('status', `Watching ${results.channel.location.city},${results.channel.location.region}`); } const forecastObj = createForecastObject(results.channel); forecastObj.woeid = woeid; cache[woeid] = forecastObj; if (callback) callback(null, forecastObj); this.emit('forecast', forecastObj); }); } }); }; this.update = (callback) => { // make sure we have something to update if (woeIds.length === 0) return; // if the last update is sooner than the update minutes, just grab it from cache const now = new Date().getTime(); if (now - lastUpdate < (config.updateMins || DEFAULT_UPDATE_MINS) * 60000) { const cacheArr = []; woeIds.forEach(id => cacheArr.push(cache[id])); if (callback) callback(null, cacheArr); this.emit('update', cacheArr); return; } // loop through all of our known woeIds and get the current forecast restler.get(ENDPOINT, { query: { q: `select * from weather.forecast where woeid in (${woeIds.join(',')})`, format: 'json', }, }) .on('complete', (response) => { if (response instanceof Error) this.emit('error', response); else { const results = response.query.results; if (results === null || results.channel === null) { const errorString = `There was a problem updating the following woeIds: ${woeIds}`; if (callback) callback (errorString); this.emit('warn', errorString); return; } // make sure we are working with an array let channel = results.channel; if(!Array.isArray(channel)) channel = [channel]; const updateArr = []; channel.forEach((info, index) => { const forecastObj = createForecastObject(info); forecastObj.woeid = woeIds[index]; cache[woeIds[index]] = forecastObj; updateArr.push(forecastObj); }); lastUpdate = new Date().getTime(); if (callback) callback(null, updateArr); this.emit('update', updateArr); } }); }; return this; }; util.inherits(Weather, EventEmitter); module.exports = Weather;