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 1.2.0 to 2.0.0

2

.vscode/launch.json

@@ -15,5 +15,5 @@ {

],
"program": "${workspaceFolder}\\demo.js"
"program": "${workspaceFolder}\\test.js"
}
]
}
var Weather = require('./weather');
/**
* Darksky is simple to use
* Simply create the weather service and then run the update function
*/
var DarkSky_Key = '0123456789abcdef9876543210fedcba'; //this is a fake key

@@ -17,62 +13,42 @@

boston.update(function(err) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('\r\n---Boston Weather (DarkSky)---');
printWeather(boston);
}
//In this example we're going to wait for the 'ready' event before running the first update.
boston.on('ready', function() {
boston.update(function(err) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('\r\n---Boston Weather (DarkSky)---');
printWeather(boston);
}
});
});
boston.on('error', function(err) {
console.log('ERROR with Boston weather lookup: ' + err);
});
/**
* AccuWeather requires a location Key, it can't run with
* lat/long or zip codes directly.
* If you know the code you can use it just like the DarkSky service.
*
* If not you need to use the helper function accuweatherLocationLookup()
* to find the key based on lat/long or zip code.
*
* This example chains the creation of the service to the callback of the
* location key lookup
*/
var AccuWeather_Key = '0123456789abcdef9876543210fedcba'; //this is a fake key
var AccuWeather_Houston; //create a reference outside the callback
//run the lookup
Weather.accuweatherLocationLookup({
var houston = new Weather.service({
provider: 'accuweather',
key: AccuWeather_Key,
latitude: 29.7604,
longitude: -95.3698,
//zipcode: 77001 //you can use a zip code or lat/long
}, function(err, locationKey) {
if (err) {
console.log('ERROR: ' + err);
} else {
//we have a valid location key now, so create the service
AccuWeather_Houston = new Weather.service({
provider: 'accuweather',
key: AccuWeather_Key,
locationKey: locationKey,
celsius: false
});
//run an update and print the result
AccuWeather_Houston.update(function(err) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('---Houston Weather (AccuWeather)---');
printWeather(AccuWeather_Houston);
}
});
}
longitude: -95.3698,
celsius: false
});
//if you call the update function before the weather service has loaded it will store
//a reference to the callback and automatically run it when the service is loaded
//so even though we're calling the update() function before the ready event was fired
//this will still work.
houston.update(function(err) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('---Houston Weather (AccuWeather)---');
printWeather(houston);
}
});
//This is just a simple function to print some information from a weather service

@@ -79,0 +55,0 @@ function printWeather(weather) {

{
"name": "jw-weather",
"version": "1.2.0",
"version": "2.0.0",
"description": "Reader for various weather APIs",

@@ -5,0 +5,0 @@ "main": "weather.js",

@@ -8,4 +8,9 @@ # jw-weather

The goal is to create a weather module that returns a standardized weather object regardless of the API used.
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).
2. Being able to switch to another service if costs or needs change.
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.
Because reason #1 is the main purpose of this module certain features that are nice to have but differ between services have been omitted. For example AccuWeather allows you to specify a zipcode for your location but DarkSky doesn't, so even though using the zipcode might be preferable vs. the latitude/longitude, that capability has been removed because it would make the usage incompatible between services.
### Sample use in Node.js:

@@ -40,8 +45,38 @@ ```

- An icon name/number
- sunrise (DarkSky only)
- sunset (DarkSky only)
- A 5-day forecast (DarkSky only)
- sunrise
- sunset
- A 5-day forecast (Accuweather does not forecast humidity)
When using AccuWeather there is a helper function for looking up location keys since AccuWeather does not use latitude/longitude in it's API call.
## Startup options:
- provider: string, 'darksky' || 'accuweather'
- key: string, provided by weather service
- latitude: number
- longitude: number
- celsius: boolean (default=false, i.e. fahrenheit)
## List of Functions:
- ### update(callback(err))
Updates the weather object with the latest forecast.
Returns an error message or null when the update is complete.
- ### fullWeather()
Returns an object representing the full standardized weather data
## List of Properties:
- raw: The raw API response from the provider
- lastUpdate: The last time the weather object was updated
- forecastTime: The time of the forecast
- temp: The current temperature
- humidity: The current humidity
- currentCondition: A text description of the current condition
- icon: An icon number
- feelsLike: The "Real Feel" temperature
- sunrise: Sunrise time
- sunset: Sunset time
- forecast: A 5-day forecast
Review demo.js for example use.

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

var darksky = new Weather.service({

@@ -63,98 +64,34 @@ provider: 'darksky',

celsius: false
}, function(err) {
if (err) {
console.log('Weather init ERR: ' + JSON.stringify(err));
} else {
console.log('Weather started!');
}
});
darksky.update(function(err) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('DARKSKY FORECAST:');
console.log('Last Update: ' + formatDate(darksky.lastUpdate, 'short') + ', ' + formatTime(darksky.lastUpdate, 'short'));
//console.log(JSON.stringify(weather.fullWeather()));
console.log('forecastTime: ' +
formatDate(darksky.forecastTime, 'short') +
', ' +
formatTime(darksky.forecastTime, 'short'));
console.log('Temp: ' + darksky.temp);
console.log('Feels like: ' + darksky.feelsLike);
console.log('Current Condition: ' + darksky.currentCondition);
console.log('Humidity: ' + darksky.humidity);
console.log('Sunrise: ' + formatTime(darksky.sunrise, 'short'));
console.log('Sunset: ' + formatTime(darksky.sunset, 'short'));
console.log('icon: ' + darksky.icon);
console.log('daily: ');
console.log('5-Day Forecast:');
console.log(JSON.stringify(darksky.forecast));
}
darksky.on('ready', function() {
console.log('Darksky Ready');
});
darksky.on('error', function(err) {
console.log('Weather Error: ' + err);
});
/*
var accuweather = null;
Weather.accuweatherLocationLookup({
key: 'xA84Kwql8VY44jdfmrqnbrWOToU2hb3W',
//latitude: 40.75855,
//longitude: -73.76543
zipcode: 11361
}, function(err, locationKey) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('LOCATION KEY: ' + locationKey);
startAccuweather(locationKey);
updateAccuweather();
}
darksky.update(function() {
console.log('DARKSKY:');
console.log(darksky.fullWeather().temp);
});
function startAccuweather(locationKey) {
accuweather = new Weather.service({
var accuweather = new Weather.service({
provider: 'accuweather',
key: 'xA84Kwql8VY44jdfmrqnbrWOToU2hb3W',
locationKey: locationKey,
latitude: 40.758556,
longitude: -73.765434,
celsius: false
}, function(err) {
if (err) {
console.log('Weather init ERR: ' + JSON.stringify(err));
} else {
console.log('Weather started!');
}
});
}
});
function updateAccuweather() {
accuweather.update(function() {
console.log('ACCUWEATHER:');
console.log(accuweather.fullWeather());
//console.log(JSON.stringify(accuweather.raw));
});
accuweather.update(function(err) {
if (err) {
console.log('ERROR: ' + err);
} else {
console.log('ACCUWEATHER FORECAST:');
console.log('Last Update: ' + formatDate(accuweather.lastUpdate, 'short') + ', ' + formatTime(accuweather.lastUpdate, 'short'));
//console.log(JSON.stringify(accuweather.fullWeather()));
console.log('forecastTime: ' +
formatDate(accuweather.forecastTime, 'short') +
', ' +
formatTime(accuweather.forecastTime, 'short'));
console.log('Temp: ' + accuweather.temp);
console.log('Feels like: ' + accuweather.feelsLike);
console.log('Current Condition: ' + accuweather.currentCondition);
console.log('Humidity: ' + accuweather.humidity);
//console.log('Sunrise: ' + formatTime(weather.sunrise, 'short'));
//console.log('Sunset: ' + formatTime(weather.sunset, 'short'));
console.log('icon: ' + accuweather.icon) ;
accuweather.on('ready', function() {
console.log('accuweather ready');
});
console.log(JSON.stringify(accuweather.raw));
}
});
}
*/

@@ -12,10 +12,8 @@ /*

* @param {'darksky'|'accuweather'} options.provider - The weather provider to use
* @param {Number} [options.latitude] - The latitude (Darksky)
* @param {Number} [options.longitude] - the longitude (Darksky)
* @param {String} [options.locationKey] - the location key (Accuweather)
* @param {Number} [options.latitude] - The latitude
* @param {Number} [options.longitude] - the longitude
* @param {boolean} [options.celsius] - Temperature in celsius
*
* @param {Function} [callback]
*/
function service(options, callback) {
function service(options) {
var _self = this;

@@ -36,18 +34,57 @@

this.forecast = [];
this.ready = false; //this is for weather services that need to do lookups
this.runUpdateWhenReady = false; //this is in case the user requests an update before the weather object is ready
this.updateWhenReadyFunc = null; //will hold the callback for the early update request
this.locationKey = null; //this is for accuweather
/******************* Custom Emitter Code **************************************************/
//this is for future browser compatibility
var _events = {};
this.on = function(event, callback) {
//attaches a callback function to an event
_events[event] = callback;
};
function emit(event, msg) {
if (typeof _events[event] === 'function') { //the client has registered the event
_events[event](msg); //run the event function provided
}
}
/*******************************************************************************************/
(function startup() {
var err = null;
if (typeof options.key !== 'string' ||
typeof options.latitude !== 'number' ||
typeof options.longitude !== 'number') {
setTimeout(function() {
emit('error', 'Invalid startup options.');
}, 100);
}
// TODO: Actually test that darksky is working before ready? Maybe just ping the service?
if (options.provider == 'darksky') {
if (typeof options.key !== 'string' ||
typeof options.latitude !== 'number' ||
typeof options.longitude !== 'number') {
err = 'Invalid startup options.';
_self.ready = true;
if (_self.runUpdateWhenReady) {
_self.update(_self.updateWhenReadyFunc);
}
setTimeout(function() {
emit('ready', null);
}, 100);
} else if (options.provider == 'accuweather') {
if (typeof options.key !== 'string' || typeof options.locationKey === 'undefined') {
err = 'Invalid startup options.';
}
accuweatherLocationLookup(options, function(err, locationKey) {
_self.locationKey = locationKey;
_self.ready = true;
if (_self.runUpdateWhenReady) {
_self.update(_self.updateWhenReadyFunc);
}
setTimeout(function() {
emit('ready', null);
}, 100);
});
}
if (typeof callback === 'function') { callback(err); }
})();

@@ -62,33 +99,73 @@

this.update = function(callback) {
var https = require('https');
var url = '';
if (options.provider == 'darksky') { //case insensitive
url = 'https://api.darksky.net/forecast/[KEY]/[LAT],[LONG]?exclude=["minutely","flags","alerts"]';
} else if (options.provider == 'accuweather') { //case insensitive
url = 'https://dataservice.accuweather.com/currentconditions/v1/[LOCATION]?apikey=[KEY]&details=true';
if (_self.ready) {
if (options.provider == 'darksky') { //case insensitive
getDarkSkyData(callback);
} else if (options.provider == 'accuweather') { //case insensitive
getAccuWeatherData(callback);
}
} else {
_self.runUpdateWhenReady = true;
_self.updateWhenReadyFunc = callback;
}
};
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)
.replace('[LONG]', options.longitude)
.replace('[LOCATION]', options.locationKey);
.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);
});
});
try {
https.get(url, function(res){
var body = '';
res.on('data', function(chunk){ body += chunk; });
res.on('end', function(){
try {
if (options.provider == 'darksky'){ parseDarkSky(body, callback); }
else if (options.provider == 'accuweather'){ parseAccuweather(body, callback); }
} catch (err) {
if (typeof callback === 'function') { callback(err); }
}
});
}).on('error', function(err) { if (typeof callback === 'function') { callback(err); } });
} catch (err) {
if (typeof callback === 'function') { callback(err); }
}
}
};
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);
}
});
}
/**

@@ -98,3 +175,3 @@ * @returns {Object} - an object representing the top-level vars

this.fullWeather = function() {
console.log('returning full weather');
//console.log('returning full weather');
var weather = {

@@ -118,2 +195,21 @@ lastUpdate: _self.lastUpdate,

function getAPIData(url, callback) {
var https = require('https');
try {
https.get(url, function(res){
var body = '';
res.on('data', function(chunk){ body += chunk; });
res.on('end', function(){
try {
body = JSON.parse(body);
if (typeof callback === 'function') { callback(null, body); }
} catch (err) {
if (typeof callback === 'function') { callback(err); }
}
});
}).on('error', function(err) { if (typeof callback === 'function') { callback(err); } });
} catch (err) {
if (typeof callback === 'function') { callback(err); }
}
}

@@ -123,3 +219,3 @@ function parseDarkSky(weather, callback) {

try {
weather = JSON.parse(weather);
//weather = JSON.parse(weather);
if (weather.error) {

@@ -174,19 +270,18 @@ if (typeof callback === 'function') {callback(weather.error); }

function parseAccuweather(weather, callback) {
function parseAccuweather(accuWeatherData, callback) {
_self.lastUpdate = new Date().toString();
_self.raw = accuWeatherData;
try {
weather = JSON.parse(weather);
weather = weather[0]; //break it out of the array
var CurrentConditions = accuWeatherData.CurrentConditions[0]; //break it out of the array
var Forecast = accuWeatherData.Forecast;
_self.raw = weather;
_self.temp = CurrentConditions.Temperature.Imperial.Value.toFixed(2);
_self.feelsLike = CurrentConditions.RealFeelTemperature.Imperial.Value.toFixed(2);
_self.currentCondition = CurrentConditions.WeatherText;
_self.humidity = (CurrentConditions.RelativeHumidity/100).toFixed(2);
_self.forecastTime = new Date(CurrentConditions.LocalObservationDateTime);
_self.sunrise = new Date(Forecast.DailyForecasts[0].Sun.Rise);
_self.sunset = new Date(Forecast.DailyForecasts[0].Sun.Set);
_self.icon = CurrentConditions.WeatherIcon;
_self.temp = weather.Temperature.Imperial.Value.toFixed(2);
_self.feelsLike = weather.RealFeelTemperature.Imperial.Value.toFixed(2);
_self.currentCondition = weather.WeatherText;
_self.humidity = (weather.RelativeHumidity/100).toFixed(2);
_self.forecastTime = new Date(weather.LocalObservationDateTime);
_self.sunrise = null;
_self.sunset = null;
_self.icon = weather.WeatherIcon;
//set celsius if desired

@@ -198,2 +293,25 @@ if (options.celsius) {

//create the forecast
for (var i=0; i<5; i++) {
var day = getDailyObject();
day.condition = Forecast.DailyForecasts[i].Day.ShortPhrase;
day.feelsLikeHigh = Forecast.DailyForecasts[i].RealFeelTemperature.Maximum.Value.toFixed(2);
day.feelsLikeLow = Forecast.DailyForecasts[i].RealFeelTemperature.Minimum.Value.toFixed(2);
day.humidity = null;
day.icon = Forecast.DailyForecasts[i].Day.Icon;
day.sunrise = new Date(Forecast.DailyForecasts[i].Sun.Rise);
day.sunset = new Date(Forecast.DailyForecasts[i].Sun.Set);
day.tempHigh = Forecast.DailyForecasts[i].Temperature.Maximum.Value.toFixed(2);
day.tempLow = Forecast.DailyForecasts[i].Temperature.Minimum.Value.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);
}
if (typeof callback === 'function') { callback(null); }

@@ -207,3 +325,3 @@

}
}
} ///END OF service object

@@ -217,3 +335,2 @@ /**

* @param {number|string} [options.longitude] - the longitude
* @param {number|string} [options.zipcode] - the zipcode
* @param {function} callback

@@ -234,7 +351,3 @@ */

var mode = null;
if (options.zipcode) {
mode = 'zipcode';
url = 'https://dataservice.accuweather.com/locations/v1/postalcodes/search?apikey=[KEY]&q=[ZIPCODE]';
} else if (options.latitude && options.longitude) {
mode = 'lat/long';
if (options.latitude && options.longitude) {
url = 'https://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=[KEY]&q=[LAT],[LONG]';

@@ -250,4 +363,3 @@ } else {

.replace('[LAT]', options.latitude)
.replace('[LONG]', options.longitude)
.replace('[ZIPCODE]', options.zipcode);
.replace('[LONG]', options.longitude);
try {

@@ -261,7 +373,3 @@ https.get(url, function(res){

var locationKey = '';
if (mode === 'zipcode') {
locationKey = res[0].Key;
} else if (mode === 'lat/long') {
locationKey = res.Key;
}
locationKey = res.Key;
if (typeof callback === 'function') { callback(null, locationKey); }

@@ -300,2 +408,1 @@ } catch (err) {

exports.service = service;
exports.accuweatherLocationLookup = accuweatherLocationLookup;
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