Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

jw-weather

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jw-weather - npm Package Compare versions

Comparing version 2.0.1 to 2.1.0

OpenWeatherMap_Example.js

13

package.json
{
"name": "jw-weather",
"version": "2.0.1",
"version": "2.1.0",
"description": "Reader for various weather APIs",

@@ -14,3 +14,7 @@ "main": "weather.js",

"keywords": [
"Weather", "DarkSky","AccuWeather","Forecast"
"Weather",
"DarkSky",
"AccuWeather",
"OpenWeatherMap",
"Forecast"
],

@@ -22,3 +26,6 @@ "author": "Jonathan Wyett",

},
"homepage": "https://github.com/jonwyett/weather#readme"
"homepage": "https://github.com/jonwyett/weather#readme",
"dependencies": {
"jw-gate": "^1.0.0"
}
}

@@ -5,7 +5,9 @@ # jw-weather

Currently supports:
- **DarkSky**
- **DarkSky** (at least until Apple shuts it down...)
- **AccuWeather**
- **OpenWeatherMap**
- ~~Weather UnderGround~~ *(API depriciated)*
The goal is to create a weather module that returns a standardized weather object regardless of the API used. This serves a few purposes:
1. Being able to seamlessly transition to another service in case the API is depreciated. (Which is why this project was started).
1. Being able to seamlessly transition to another service in case the API is depreciated. *Which is why this project was started, and is already happening again with the DarkSky API.*
2. Being able to switch to another service if costs or needs change.

@@ -51,3 +53,3 @@ 3. Being able to use more then one service simultaneously to increase the frequency that you are able to query the APIs since the providers limit allowed queries for a given license.

- provider: string, 'darksky' || 'accuweather'
- provider: string, 'darksky' || 'accuweather' || 'openweathermap'
- key: string, provided by weather service

@@ -54,0 +56,0 @@ - latitude: number

@@ -56,5 +56,5 @@ function formatDate(fullDate, formatString) {

var darksky = new Weather.service({
provider: 'darksky',
/*
var openWeather = new Weather.service({
provider: 'OpenWeatherMap',
key: '',

@@ -66,32 +66,94 @@ latitude: 40.758556,

darksky.on('ready', function() {
console.log('Darksky Ready');
openWeather.on('ready', function() {
console.log('openWeather Ready');
console.log('...update openWeather...');
openWeather.update(function() {
console.log('openWeather listing:');
console.log(openWeather.fullWeather().temp);
});
});
darksky.on('error', function(err) {
openWeather.on('error', function(err) {
console.log('Weather Error: ' + err);
});
darksky.update(function() {
console.log('DARKSKY:');
console.log(darksky.fullWeather().temp);
*/
/*
var accuWeather = new Weather.service({
provider: 'accuWeather',
key: '',
latitude: 40.758556,
longitude: -73.765434,
celsius: false
});
var accuweather = new Weather.service({
provider: 'accuweather',
accuWeather.on('ready', function() {
console.log('accuWeather Ready');
console.log('...update accuWeather...');
accuWeather.update(function() {
console.log('accuWeather listing:');
console.log(accuWeather.fullWeather().temp);
});
});
accuWeather.on('error', function(err) {
console.log('accuWeather Error: ' + err);
});
*/
/*
var darkSky = new Weather.service({
provider: 'darkSky',
key: '',
latitude: 40.758556,
longitude: -73.765434,
longitude: -73.765434,
celsius: false
});
accuweather.update(function() {
console.log('ACCUWEATHER:');
console.log(accuweather.fullWeather());
//console.log(JSON.stringify(accuweather.raw));
darkSky.on('ready', function() {
console.log('darkSky Ready');
console.log('...update darkSky...');
darkSky.update(function() {
console.log('darkSky listing:');
console.log(darkSky.fullWeather().temp);
});
});
accuweather.on('ready', function() {
console.log('accuweather ready');
darkSky.on('error', function(err) {
console.log('darkSky Error: ' + err);
});
*/
var weatherbit = new Weather.service({
provider: 'weatherbit',
key: '',
latitude: 40.758556,
longitude: -73.765434,
celsius: false
});
weatherbit.on('ready', function() {
console.log('weatherbit Ready');
console.log('...update weatherbit...');
weatherbit.update(function() {
console.log('weatherbit listing:');
console.log(weatherbit.fullWeather());
//console.log(formatTime(weatherbit.sunrise));
});
});
weatherbit.on('error', function(err) {
console.log('weatherbit Error: ' + err);
});
/*
ver 2.1.0
-Add OpenWetherMap support
-Add WeatherBit support
-Complete rewrite
-require jw-gate
ver 1.0.2

@@ -6,2 +12,4 @@ -includes celsius option

var jwGate = require('jw-gate');
/**

@@ -12,3 +20,3 @@ * Creates a service for various online weather APIs

* @param {String} options.key - API Key
* @param {'darksky'|'accuweather'} options.provider - The weather provider to use
* @param {'darksky'|'accuweather'|'openweathermap'|weatherbit} options.provider - The weather provider to use
* @param {Number} [options.latitude] - The latitude

@@ -24,2 +32,6 @@ * @param {Number} [options.longitude] - the longitude

this.raw = {}; //this is the raw API response from the provider
//an object to hold the various data elements and urls to retrieve the data
this.weatherData = {};
this.lastUpdate = null;

@@ -66,5 +78,7 @@ this.forecastTime = null;

}
options.provider = options.provider.toLowerCase();
// TODO: Actually test that darksky is working before ready? Maybe just ping the service?
if (options.provider == 'darksky') {
if (options.provider === 'darksky' ||
options.provider === 'openweathermap' ||
options.provider === 'weatherbit') {

@@ -80,3 +94,3 @@ _self.ready = true;

} else if (options.provider == 'accuweather') {
} else if (options.provider === 'accuweather') {
accuweatherLocationLookup(options, function(err, locationKey) {

@@ -103,7 +117,3 @@ _self.locationKey = locationKey;

if (_self.ready) {
if (options.provider == 'darksky') { //case insensitive
getDarkSkyData(callback);
} else if (options.provider == 'accuweather') { //case insensitive
getAccuWeatherData(callback);
}
getWeatherData(callback);
} else {

@@ -115,62 +125,4 @@ _self.runUpdateWhenReady = true;

function getDarkSkyData(callback) {
var url = 'https://api.darksky.net/forecast/[KEY]/[LAT],[LONG]?exclude=["minutely","flags","alerts"]';
url = url.replace('[KEY]',options.key)
.replace('[LAT]', options.latitude.toString())
.replace('[LONG]', options.longitude.toString());
getAPIData(url, function(err, weather) {
if (err) {
callback(err);
} else {
parseDarkSky(weather, callback);
}
});
}
function getAccuWeatherData(callback) {
var url = '';
//accuweather needs multiple calls, so we'll store them in an object
var accuWeatherData = {
CurrentConditions : {},
Forecast: {}
};
getAccuWeatherCurrentConditions(accuWeatherData, function(accuWeatherData) {
getAccuWeatherForecast(accuWeatherData, function(accuWeatherData) {
parseAccuweather(accuWeatherData, callback);
});
});
}
function getAccuWeatherCurrentConditions(accuWeatherData, callback) {
url = 'https://dataservice.accuweather.com/currentconditions/v1/[LOCATION]?apikey=[KEY]&details=true';
url = url.replace('[KEY]',options.key)
.replace('[LOCATION]', _self.locationKey);
getAPIData(url, function(err, weather) {
if (err) {
callback(err);
} else {
accuWeatherData.CurrentConditions = weather;
callback(accuWeatherData);
}
});
}
function getAccuWeatherForecast(accuWeatherData, callback) {
url = 'https://dataservice.accuweather.com/forecasts/v1/daily/5day/[LOCATION]?apikey=[KEY]&details=true';
url = url.replace('[KEY]',options.key)
.replace('[LOCATION]', _self.locationKey);
getAPIData(url, function(err, weather) {
if (err) {
callback(err);
} else {
accuWeatherData.Forecast = weather;
callback(accuWeatherData);
}
});
}
/**
/**
* @returns {Object} - an object representing the top-level vars

@@ -198,2 +150,98 @@ */

function getWeatherData(callback) {
/*
This function is perhaps to clever for it's own good. This is bad. I will attempt to explain the design:
Each weather provider has 1 or more different API calls that are required to retrieve the standard data.
1) Create a weather object with all of the urls needed.
2) Create a jw-gate object using the weather object's keys as lock names
jw-gate is a simple class for running sync events. When all the "locks" on the gate become unlocked
the gate itself becomes unlocked and whatever needs to happen after all of the sync events are completed
happens.
3) Iterate through the weather object and get the data. This step is the confusing one because after the
data is retrieved the url in the weather object will be replaced with the data itself, so in simple terms:
weather {
Forecast: 'https://API.com'
}
becomes:
weather {
ForeCast: {
temp: 25.45,
humidity: 48
}
}
4) Parse the weather data using a unique parser for that API
5) Run the callback function
*/
//will be null if the parser is successful or will contain an error
var err = null;
//fill the weatherData object with the needed API calls
if (options.provider === 'openweathermap') {
weatherData = {
CurrentConditions: 'https://api.openweathermap.org/data/2.5/weather?units=imperial&lat=[LAT]&lon=[LONG]&appid=[KEY]',
Forecast: 'https://api.openweathermap.org/data/2.5/forecast?units=imperial&lat=[LAT]&lon=[LONG]&appid=[KEY]'
};
} else if (options.provider === 'accuweather') {
weatherData = {
CurrentConditions: 'https://dataservice.accuweather.com/currentconditions/v1/[LOCATION]?apikey=[KEY]&details=true',
Forecast: 'https://dataservice.accuweather.com/forecasts/v1/daily/5day/[LOCATION]?apikey=[KEY]&details=true'
};
} else if (options.provider === 'darksky') {
weatherData = {
Forecast: 'https://api.darksky.net/forecast/[KEY]/[LAT],[LONG]?exclude=["minutely","flags","alerts"]'
};
} else if (options.provider === 'weatherbit') {
weatherData = {
Forecast: 'https://api.weatherbit.io/v2.0/forecast/daily?lat=[LAT]&lon=[LONG]&days=5&units=I&key=[KEY]',
CurrentConditions: 'https://api.weatherbit.io/v2.0/current?lat=40.758556&lon=-73.765434&units=I&key=bd2295d6056e46a0adb4f2ff43569e03'
};
}
//get an array holding the weatherData keys
var apiCalls = Object.keys(weatherData);
//create a gate to allow for simultaneous API calls using the keys
var apiGate = new jwGate.Gate(apiCalls, true);
//this will fire when all the API calls are complete
apiGate.on('unlocked', function() {
if (options.provider === 'openweathermap') {
err = parseOpenWeatherMap();
} else if (options.provider === 'accuweather') {
err = parseAccuweather();
} else if (options.provider === 'darksky') {
err = parseDarkSky();
} else if (options.provider === 'weatherbit') {
err = parseWeatherBit();
}
//emit the error if it has been set
if (err) { emit('error', err); }
//run the callback function
if (typeof callback === 'function') { callback(err); }
});
//replace the values in the url and get the data
apiCalls.forEach(function(url) {
weatherData[url] = weatherData[url].replace('[KEY]',options.key)
.replace('[LAT]', options.latitude.toString())
.replace('[LONG]', options.longitude.toString())
.replace('[LOCATION]', _self.locationKey);
//console.log(weatherData[url]);
getAPIData(weatherData[url], function(err, weather) {
if (err) {
callback(err);
} else {
//this replaces the url with the weather data retrieved from the API
weatherData[url] = weather;
apiGate.lock(url, false);
}
});
});
}
function getAPIData(url, callback) {

@@ -219,6 +267,6 @@ var https = require('https');

function parseDarkSky(weather, callback) {
function parseDarkSky() {
_self.lastUpdate = new Date().toString();
try {
//weather = JSON.parse(weather);
var weather = weatherData.Forecast;
if (weather.error) {

@@ -265,16 +313,16 @@ if (typeof callback === 'function') {callback(weather.error); }

if (typeof callback === 'function') { callback(null); }
return null;
}
} catch (err) {
_self.error = err;
if (typeof callback === 'function') { callback(err); }
return err;
}
}
function parseAccuweather(accuWeatherData, callback) {
function parseAccuweather() {
_self.lastUpdate = new Date().toString();
_self.raw = accuWeatherData;
_self.raw = weatherData;
try {
var CurrentConditions = accuWeatherData.CurrentConditions[0]; //break it out of the array
var Forecast = accuWeatherData.Forecast;
var CurrentConditions = weatherData.CurrentConditions[0]; //break it out of the array
var Forecast = weatherData.Forecast;

@@ -319,10 +367,120 @@ _self.temp = CurrentConditions.Temperature.Imperial.Value.toFixed(2);

if (typeof callback === 'function') { callback(null); }
return null;
} catch (err) {
_self.error = err;
if (typeof callback === 'function') { callback(err); }
return err;
}
}
function parseOpenWeatherMap() {
_self.lastUpdate = new Date().toString();
try {
var CurrentConditions = weatherData.CurrentConditions;
var Forecast = weatherData.Forecast;
_self.raw = weatherData;
_self.temp = CurrentConditions.main.temp.toFixed(2);
_self.feelsLike = CurrentConditions.main.feels_like.toFixed(2);
_self.currentCondition = CurrentConditions.weather[0].description;
_self.humidity = CurrentConditions.main.humidity.toFixed(2);
//_self.forecastTime = new Date(CurrentConditions * 1000);
_self.sunrise = new Date(CurrentConditions.sys.sunrise * 1000);
_self.sunset = new Date(CurrentConditions.sys.sunset * 1000);
_self.icon = CurrentConditions.weather[0].icon;
//set celsius if desired
if (options.celsius) {
_self.temp = toCelsius(_self.temp);
_self.feelsLike = toCelsius(_self.feelsLike);
}
//create the forecast
for (var i=0; i<5; i++) {
var day = getDailyObject();
day.condition = Forecast.list[i].weather[0].description;
day.feelsLikeHigh = Forecast.list[i].main.temp_max.toFixed(2);
day.feelsLikeLow = Forecast.list[i].main.temp_min.toFixed(2);
day.humidity = Forecast.list[i].main.humidity.toFixed(2);
day.icon = null;
day.sunrise = null;
day.sunset = null;
day.tempHigh = day.feelsLikeHigh;
day.tempLow =day.feelsLikeLow;
if (options.celsius) {
day.feelsLikeHigh = toCelsius(day.feelsLikeHigh);
day.feelsLikeLow = toCelsius(day.feelsLikeLow);
day.tempHigh = toCelsius(day.tempHigh);
day.tempLow = toCelsius(day.tempLow);
}
_self.forecast.push(day);
}
return null;
} catch (err) {
_self.error = err;
return err;
}
}
function parseWeatherBit() {
_self.lastUpdate = new Date().toString();
try {
_self.raw = weatherData;
var CurrentConditions = weatherData.CurrentConditions.data[0];
var Forecast = weatherData.Forecast;
_self.temp = CurrentConditions.temp.toFixed(2);
_self.feelsLike = CurrentConditions.app_temp.toFixed(2);
_self.currentCondition = CurrentConditions.weather.description;
_self.humidity = CurrentConditions.rh.toFixed(2);
_self.forecastTime = new Date(CurrentConditions.ts * 1000);
_self.sunrise = new Date(Forecast.data[0].sunrise_ts * 1000);
_self.sunset = new Date(Forecast.data[0].sunset_ts * 1000);
_self.icon = CurrentConditions.weather.icon;
//set celsius if desired
if (options.celsius) {
_self.temp = toCelsius(_self.temp);
_self.feelsLike = toCelsius(_self.feelsLike);
}
//create the forecast
for (var i=0; i<5; i++) {
var day = getDailyObject();
day.condition = Forecast.data[i].weather.description;
day.feelsLikeHigh = Forecast.data[i].app_max_temp.toFixed(2);
day.feelsLikeLow = Forecast.data[i].app_min_temp.toFixed(2);
day.humidity = Forecast.data[i].rh.toFixed(2);
day.icon = Forecast.data[i].weather.icon;
day.sunrise = new Date(Forecast.data[i].sunrise_ts * 1000);
day.sunset = new Date(Forecast.data[i].sunset_ts * 1000);
day.tempHigh = Forecast.data[i].max_temp.toFixed(2);
day.tempLow = Forecast.data[i].low_temp.toFixed(2);
if (options.celsius) {
day.feelsLikeHigh = toCelsius(day.feelsLikeHigh);
day.feelsLikeLow = toCelsius(day.feelsLikeLow);
day.tempHigh = toCelsius(day.tempHigh);
day.tempLow = toCelsius(day.tempLow);
}
_self.forecast.push(day);
}
return null;
} catch (err) {
_self.error = err;
return err;
}
}
} ///END OF service object

@@ -329,0 +487,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc