Seperate OWM calls for App and OS
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ node_modules
|
|||||||
.env
|
.env
|
||||||
coverage/*
|
coverage/*
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
.vscode
|
||||||
|
|
||||||
# Elastic Beanstalk Files
|
# Elastic Beanstalk Files
|
||||||
.elasticbeanstalk/*
|
.elasticbeanstalk/*
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var http = require( "http" ),
|
|||||||
time: /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-])(\d{2})(\d{2})/,
|
time: /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-])(\d{2})(\d{2})/,
|
||||||
timezone: /^()()()()()()([+-])(\d{2})(\d{2})/
|
timezone: /^()()()()()()([+-])(\d{2})(\d{2})/
|
||||||
};
|
};
|
||||||
|
|
||||||
// If location does not match GPS or PWS/ICAO, then attempt to resolve
|
// If location does not match GPS or PWS/ICAO, then attempt to resolve
|
||||||
// location using Weather Underground autocomplete API
|
// location using Weather Underground autocomplete API
|
||||||
function resolveCoordinates( location, callback ) {
|
function resolveCoordinates( location, callback ) {
|
||||||
@@ -38,58 +38,102 @@ function resolveCoordinates( location, callback ) {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve weather data from Open Weather Map
|
function getData( url, callback ) {
|
||||||
function getOWMWeatherData( location, callback ) {
|
|
||||||
|
|
||||||
// Generate URL using OpenWeatherMap in Imperial units
|
httpRequest( url, function( data ) {
|
||||||
|
try {
|
||||||
|
data = JSON.parse( data );
|
||||||
|
} catch (err) {
|
||||||
|
callback( {} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback( data );
|
||||||
|
return;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve data from Open Weather Map for water level calculations
|
||||||
|
function getOWMWateringData( location, callback ) {
|
||||||
var OWM_API_KEY = process.env.OWM_API_KEY,
|
var OWM_API_KEY = process.env.OWM_API_KEY,
|
||||||
forecastUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?appid=" + OWM_API_KEY + "&units=imperial&lat=" + location[ 0 ] + "&lon=" + location[ 1 ];
|
forecastUrl = "http://api.openweathermap.org/data/2.5/forecast?appid=" + OWM_API_KEY + "&units=imperial&lat=" + location[ 0 ] + "&lon=" + location[ 1 ];
|
||||||
|
|
||||||
getTimeData( location, function( weather ) {
|
getTimeData( location, function( weather ) {
|
||||||
|
|
||||||
// Perform the HTTP request to retrieve the weather data
|
// Perform the HTTP request to retrieve the weather data
|
||||||
httpRequest( forecastUrl, function( data ) {
|
getData( forecastUrl, function( forecast ) {
|
||||||
try {
|
|
||||||
data = JSON.parse( data );
|
|
||||||
} catch ( err ) {
|
|
||||||
|
|
||||||
// Otherwise indicate the request failed
|
if ( !forecast || !forecast.list ) {
|
||||||
callback( weather );
|
callback( weather );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !data.list ) {
|
weather.temp = 0;
|
||||||
callback(weather);
|
weather.humidity = 0;
|
||||||
return;
|
weather.precip = 0;
|
||||||
|
|
||||||
|
var periods = Math.min(forecast.list.length, 10);
|
||||||
|
for ( var index = 0; index < periods; index++ ) {
|
||||||
|
weather.temp += parseFloat( forecast.list[ index ].main.temp );
|
||||||
|
weather.humidity += parseInt( forecast.list[ index ].main.humidity );
|
||||||
|
weather.precip += ( forecast.list[ index ].rain ? parseFloat( forecast.list[ index ].rain[ "3h" ] || 0 ) : 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
weather.region = data.city.country;
|
weather.temp = weather.temp / periods;
|
||||||
weather.city = data.city.name;
|
weather.humidity = weather.humidity / periods;
|
||||||
weather.minTemp = parseInt( data.list[ 0 ].temp.min );
|
weather.precip = weather.precip / 25.4;
|
||||||
weather.maxTemp = parseInt( data.list[ 0 ].temp.max );
|
weather.raining = ( forecast.list[ 0 ].rain ? ( parseFloat( forecast.list[ 0 ].rain[ "3h" ] || 0 ) > 0 ) : false );
|
||||||
weather.temp = ( weather.minTemp + weather.maxTemp ) / 2;
|
|
||||||
weather.humidity = parseInt( data.list[ 0 ].humidity );
|
|
||||||
weather.wind = parseInt( data.list[ 0 ].speed );
|
|
||||||
weather.precip = ( data.list[ 0 ].rain ? parseFloat( data.list[ 0 ].rain || 0 ) : 0 ) / 25.4;
|
|
||||||
weather.description = data.list[ 0 ].weather[ 0 ].description;
|
|
||||||
weather.icon = data.list[ 0 ].weather[ 0 ].icon;
|
|
||||||
weather.forecast = [];
|
|
||||||
|
|
||||||
for ( var index = 0; index < data.list.length; index++ ) {
|
|
||||||
weather.forecast.push( {
|
|
||||||
temp_min: parseInt( data.list[ index ].temp.min ),
|
|
||||||
temp_max: parseInt( data.list[ index ].temp.max ),
|
|
||||||
date: parseInt( data.list[ index ].dt ),
|
|
||||||
icon: data.list[ index ].weather[ 0 ].icon,
|
|
||||||
description: data.list[ index ].weather[ 0 ].description
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
callback( weather );
|
callback( weather );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve weather data from Open Weather Map for App
|
||||||
|
function getOWMWeatherData( location, callback ) {
|
||||||
|
var OWM_API_KEY = process.env.OWM_API_KEY,
|
||||||
|
currentUrl = "http://api.openweathermap.org/data/2.5/weather?appid=" + OWM_API_KEY + "&units=imperial&lat=" + location[ 0 ] + "&lon=" + location[ 1 ],
|
||||||
|
forecastDailyUrl = "http://api.openweathermap.org/data/2.5/forecast/daily?appid=" + OWM_API_KEY + "&units=imperial&lat=" + location[ 0 ] + "&lon=" + location[ 1 ];
|
||||||
|
|
||||||
|
getTimeData( location, function( weather ) {
|
||||||
|
|
||||||
|
getData( currentUrl, function( current ) {
|
||||||
|
|
||||||
|
getData( forecastDailyUrl, function( forecast ) {
|
||||||
|
|
||||||
|
if ( !current || !current.main || !current.wind || !current.weather || !forecast || !forecast.list ) {
|
||||||
|
callback( weather );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
weather.temp = parseInt( current.main.temp );
|
||||||
|
weather.humidity = parseInt( current.main.humidity );
|
||||||
|
weather.wind = parseInt( current.wind.speed );
|
||||||
|
weather.description = current.weather[0].description;
|
||||||
|
weather.icon = current.weather[0].icon;
|
||||||
|
|
||||||
|
weather.region = forecast.city.country;
|
||||||
|
weather.city = forecast.city.name;
|
||||||
|
weather.minTemp = parseInt( forecast.list[ 0 ].temp.min );
|
||||||
|
weather.maxTemp = parseInt( forecast.list[ 0 ].temp.max );
|
||||||
|
weather.precip = ( forecast.list[ 0 ].rain ? parseFloat( forecast.list[ 0 ].rain || 0 ) : 0 ) / 25.4;
|
||||||
|
weather.forecast = [];
|
||||||
|
|
||||||
|
for ( var index = 0; index < forecast.list.length; index++ ) {
|
||||||
|
weather.forecast.push( {
|
||||||
|
temp_min: parseInt( forecast.list[ index ].temp.min ),
|
||||||
|
temp_max: parseInt( forecast.list[ index ].temp.max ),
|
||||||
|
date: parseInt( forecast.list[ index ].dt ),
|
||||||
|
icon: forecast.list[ index ].weather[ 0 ].icon,
|
||||||
|
description: forecast.list[ index ].weather[ 0 ].description
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
callback( weather );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate timezone and sun rise/set information
|
// Calculate timezone and sun rise/set information
|
||||||
function getTimeData( location, callback ) {
|
function getTimeData( location, callback ) {
|
||||||
var timezone = moment().tz( geoTZ( location[ 0 ], location[ 1 ] ) ).utcOffset();
|
var timezone = moment().tz( geoTZ( location[ 0 ], location[ 1 ] ) ).utcOffset();
|
||||||
@@ -175,24 +219,7 @@ function checkWeatherRestriction( adjustmentValue, weather ) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the weather indicates it is raining and returns a boolean representation
|
exports.getWeatherData = function( req, res ) {
|
||||||
function checkRainStatus( weather ) {
|
|
||||||
|
|
||||||
// Define all the weather codes that indicate rain
|
|
||||||
var adverseCodes = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 ],
|
|
||||||
adverseWords = [ "flurries", "sleet", "rain", "sleet", "snow", "tstorms" ];
|
|
||||||
|
|
||||||
if ( ( weather.iconCode && adverseCodes.indexOf( weather.iconCode ) !== -1 ) || ( weather.icon && adverseWords.indexOf( weather.icon ) !== -1 ) ) {
|
|
||||||
|
|
||||||
// If the current weather indicates rain, add a rain delay flag to the weather script indicating
|
|
||||||
// the controller should not water.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.showWeatherData = function( req, res ) {
|
|
||||||
var location = req.query.loc;
|
var location = req.query.loc;
|
||||||
|
|
||||||
if ( filters.gps.test( location ) ) {
|
if ( filters.gps.test( location ) ) {
|
||||||
@@ -228,7 +255,7 @@ exports.showWeatherData = function( req, res ) {
|
|||||||
// API Handler when using the weatherX.py where X represents the
|
// API Handler when using the weatherX.py where X represents the
|
||||||
// adjustment method which is encoded to also carry the watering
|
// adjustment method which is encoded to also carry the watering
|
||||||
// restriction and therefore must be decoded
|
// restriction and therefore must be decoded
|
||||||
exports.getWeather = function( req, res ) {
|
exports.getWateringData = function( req, res ) {
|
||||||
|
|
||||||
// The adjustment method is encoded by the OpenSprinkler firmware and must be
|
// The adjustment method is encoded by the OpenSprinkler firmware and must be
|
||||||
// parsed. This allows the adjustment method and the restriction type to both
|
// parsed. This allows the adjustment method and the restriction type to both
|
||||||
@@ -262,7 +289,7 @@ exports.getWeather = function( req, res ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If any weather adjustment is being used, check the rain status
|
// If any weather adjustment is being used, check the rain status
|
||||||
if ( adjustmentMethod > 0 && checkRainStatus( weather ) ) {
|
if ( adjustmentMethod > 0 && weather.hasOwnProperty( "raining" ) && weather.raining ) {
|
||||||
|
|
||||||
// If it is raining and the user has weather-based rain delay as the adjustment method then apply the specified delay
|
// If it is raining and the user has weather-based rain delay as the adjustment method then apply the specified delay
|
||||||
if ( adjustmentMethod === 2 ) {
|
if ( adjustmentMethod === 2 ) {
|
||||||
@@ -335,7 +362,7 @@ exports.getWeather = function( req, res ) {
|
|||||||
location = [ parseFloat( location[ 0 ] ), parseFloat( location[ 1 ] ) ];
|
location = [ parseFloat( location[ 0 ] ), parseFloat( location[ 1 ] ) ];
|
||||||
|
|
||||||
// Continue with the weather request
|
// Continue with the weather request
|
||||||
getOWMWeatherData( location, finishRequest );
|
getOWMWateringData( location, finishRequest );
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Attempt to resolve provided location to GPS coordinates when it does not match
|
// Attempt to resolve provided location to GPS coordinates when it does not match
|
||||||
@@ -347,7 +374,7 @@ exports.getWeather = function( req, res ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location = result;
|
location = result;
|
||||||
getOWMWeatherData( location, finishRequest );
|
getOWMWateringData( location, finishRequest );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ if ( !process.env.HOST || !process.env.PORT ) {
|
|||||||
// Handle requests matching /weatherID.py where ID corresponds to the
|
// Handle requests matching /weatherID.py where ID corresponds to the
|
||||||
// weather adjustment method selector.
|
// weather adjustment method selector.
|
||||||
// This endpoint is considered deprecated and supported for prior firmware
|
// This endpoint is considered deprecated and supported for prior firmware
|
||||||
app.get( /weather(\d+)\.py/, weather.getWeather );
|
app.get( /weather(\d+)\.py/, weather.getWateringData );
|
||||||
app.get( /(\d+)/, weather.getWeather );
|
app.get( /(\d+)/, weather.getWateringData );
|
||||||
|
|
||||||
// Handle requests matching /weatherData
|
// Handle requests matching /weatherData
|
||||||
app.options( /weatherData/, cors() );
|
app.options( /weatherData/, cors() );
|
||||||
app.get( /weatherData/, cors(), weather.showWeatherData );
|
app.get( /weatherData/, cors(), weather.getWeatherData );
|
||||||
|
|
||||||
app.get( "/", function( req, res ) {
|
app.get( "/", function( req, res ) {
|
||||||
res.send( "OpenSprinkler Weather Service" );
|
res.send( "OpenSprinkler Weather Service" );
|
||||||
|
|||||||
Reference in New Issue
Block a user