Add WUnderground WeatherProvider with Zimmerman PWS support

This commit is contained in:
Matthew Oslan
2019-06-28 16:02:35 -04:00
parent dc171ebe68
commit 5f35b0410c
6 changed files with 99 additions and 13 deletions

View File

@@ -5,13 +5,14 @@ import * as SunCalc from "suncalc";
import * as moment from "moment-timezone";
import * as geoTZ from "geo-tz";
import { GeoCoordinates, TimeData, WeatherData, BaseWateringData } from "../types";
import { BaseWateringData, GeoCoordinates, PWS, TimeData, WeatherData } from "../types";
import { WeatherProvider } from "./weatherProviders/WeatherProvider";
import { AdjustmentMethod, AdjustmentMethodResponse, AdjustmentOptions } from "./adjustmentMethods/AdjustmentMethod";
import ManualAdjustmentMethod from "./adjustmentMethods/ManualAdjustmentMethod";
import ZimmermanAdjustmentMethod from "./adjustmentMethods/ZimmermanAdjustmentMethod";
import RainDelayAdjustmentMethod from "./adjustmentMethods/RainDelayAdjustmentMethod";
const weatherProvider: WeatherProvider = new ( require("./weatherProviders/" + ( process.env.WEATHER_PROVIDER || "OWM" ) ).default )();
const WEATHER_PROVIDER: WeatherProvider = new ( require("./weatherProviders/" + ( process.env.WEATHER_PROVIDER || "OWM" ) ).default )();
const PWS_WEATHER_PROVIDER: WeatherProvider = new ( require("./weatherProviders/" + ( process.env.PWS_WEATHER_PROVIDER || "WUnderground" ) ).default )();
// Define regex filters to match against location
const filters = {
@@ -42,7 +43,7 @@ async function resolveCoordinates( location: string ): Promise< GeoCoordinates >
}
if ( filters.pws.test( location ) ) {
throw "Weather Underground is discontinued";
throw "PWS ID must be specified in the pws parameter.";
} else if ( filters.gps.test( location ) ) {
const split: string[] = location.split( "," );
return [ parseFloat( split[ 0 ] ), parseFloat( split[ 1 ] ) ];
@@ -154,7 +155,7 @@ export const getWeatherData = async function( req: express.Request, res: express
const timeData: TimeData = getTimeData( coordinates );
let weatherData: WeatherData;
try {
weatherData = await weatherProvider.getWeatherData( coordinates );
weatherData = await WEATHER_PROVIDER.getWeatherData( coordinates );
} catch ( err ) {
res.send( "Error: " + err );
return;
@@ -181,6 +182,7 @@ export const getWateringData = async function( req: express.Request, res: expres
location: string | GeoCoordinates = getParameter(req.query.loc),
outputFormat: string = getParameter(req.query.format),
remoteAddress: string = getParameter(req.headers[ "x-forwarded-for" ]) || req.connection.remoteAddress,
pwsString: string = getParameter( req.query.pws ),
adjustmentOptions: AdjustmentOptions;
// X-Forwarded-For header may contain more than one IP address and therefore
@@ -213,10 +215,22 @@ export const getWateringData = async function( req: express.Request, res: expres
let timeData: TimeData = getTimeData( coordinates );
// Parse the PWS information.
let pws: PWS | undefined = undefined;
if ( pwsString ) {
try {
pws = parsePWS( pwsString );
} catch ( err ) {
res.send( `Error: ${ err }` );
return;
}
}
const weatherProvider = pws ? PWS_WEATHER_PROVIDER : WEATHER_PROVIDER;
let adjustmentMethodResponse: AdjustmentMethodResponse;
try {
adjustmentMethodResponse = await adjustmentMethod.calculateWateringScale(
adjustmentOptions, coordinates, weatherProvider
adjustmentOptions, coordinates, weatherProvider, pws
);
} catch ( err ) {
if ( typeof err != "string" ) {
@@ -431,3 +445,18 @@ function getParameter( parameter: string | string[] ): string {
// Return an empty string if the parameter is undefined.
return parameter || "";
}
/**
* Creates a PWS object from a string.
* @param pwsString Information about the PWS in the format "pws:API_KEY@PWS_ID".
* @return The PWS specified by the string.
* @throws Throws an error message if the string is in an invalid format and cannot be parsed.
*/
function parsePWS( pwsString: string): PWS {
const match = pwsString.match( /^pws:(?<apiKey>[a-f\d]{32})@(?<id>[a-zA-Z\d]+)$/ );
if ( !match ) {
throw "Invalid PWS format.";
}
return match.groups as PWS;
}