Refactor adjustment method selection
This commit is contained in:
58
routes/adjustmentMethods/AdjustmentMethod.ts
Normal file
58
routes/adjustmentMethods/AdjustmentMethod.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { GeoCoordinates, WateringData } from "../../types";
|
||||||
|
import { WeatherProvider } from "../weatherProviders/WeatherProvider";
|
||||||
|
|
||||||
|
|
||||||
|
export interface AdjustmentMethod {
|
||||||
|
/**
|
||||||
|
* Calculates the percentage that should be used to scale watering time.
|
||||||
|
* @param adjustmentOptions The user-specified options for the calculation, or undefined/null if no custom values
|
||||||
|
* are to be used. No checks will be made to ensure the AdjustmentOptions are the correct type that the function
|
||||||
|
* is expecting or to ensure that any of its fields are valid.
|
||||||
|
* @param wateringData The basic weather information of the watering site. This may be undefined if an error occurred
|
||||||
|
* while retrieving the data.
|
||||||
|
* @param coordinates The coordinates of the watering site.
|
||||||
|
* @param weatherProvider The WeatherProvider that should be used if the adjustment method needs to obtain any more
|
||||||
|
* weather data.
|
||||||
|
* @return A Promise that will be resolved with the result of the calculation, or rejected with an error message if
|
||||||
|
* the watering scale cannot be calculated.
|
||||||
|
* @throws An error message can be thrown if an error occurs while calculating the watering scale.
|
||||||
|
*/
|
||||||
|
calculateWateringScale(
|
||||||
|
adjustmentOptions: AdjustmentOptions,
|
||||||
|
wateringData: WateringData | undefined,
|
||||||
|
coordinates: GeoCoordinates,
|
||||||
|
weatherProvider: WeatherProvider
|
||||||
|
): Promise< AdjustmentMethodResponse >;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdjustmentMethodResponse {
|
||||||
|
/**
|
||||||
|
* The percentage that should be used to scale the watering level. This should be an integer between 0-200 (inclusive),
|
||||||
|
* or undefined if the watering level should not be changed.
|
||||||
|
*/
|
||||||
|
scale: number | undefined;
|
||||||
|
/**
|
||||||
|
* The raw data that was used to calculate the watering scale. This will be sent directly to the OS controller, so
|
||||||
|
* each field should be formatted in a way that the controller understands and numbers should be rounded
|
||||||
|
* appropriately to remove excessive figures. If no data was used (e.g. an error occurred), this should be undefined.
|
||||||
|
*/
|
||||||
|
rawData?: object;
|
||||||
|
/**
|
||||||
|
* How long watering should be delayed for (in hours) due to rain, or undefined if watering should not be delayed
|
||||||
|
* for a specific amount of time (either it should be delayed indefinitely or it should not be delayed at all). This
|
||||||
|
* property will not stop watering on its own, and the `scale` property should be set to 0 to actually prevent
|
||||||
|
* watering.
|
||||||
|
*/
|
||||||
|
rainDelay?: number;
|
||||||
|
// TODO consider removing this field and breaking backwards compatibility to handle all errors consistently.
|
||||||
|
/**
|
||||||
|
* An message to send to the OS firmware to indicate that an error occurred while calculating the watering
|
||||||
|
* scale and the returned scale either defaulted to some reasonable value or was calculated with incomplete data.
|
||||||
|
* Older firmware versions will ignore this field (they will silently swallow the error and use the returned scale),
|
||||||
|
* but newer firmware versions may be able to alert the user that an error occurred and/or default to a
|
||||||
|
* user-configured watering scale instead of using the one returned by the AdjustmentMethod.
|
||||||
|
*/
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdjustmentOptions {}
|
||||||
17
routes/adjustmentMethods/ManualAdjustmentMethod.ts
Normal file
17
routes/adjustmentMethods/ManualAdjustmentMethod.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { AdjustmentMethod, AdjustmentMethodResponse } from "./AdjustmentMethod";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not change the watering scale (only time data will be returned).
|
||||||
|
*/
|
||||||
|
async function calculateManualWateringScale( ): Promise< AdjustmentMethodResponse > {
|
||||||
|
return {
|
||||||
|
scale: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ManualAdjustmentMethod: AdjustmentMethod = {
|
||||||
|
calculateWateringScale: calculateManualWateringScale
|
||||||
|
};
|
||||||
|
export default ManualAdjustmentMethod;
|
||||||
27
routes/adjustmentMethods/RainDelayAdjustmentMethod.ts
Normal file
27
routes/adjustmentMethods/RainDelayAdjustmentMethod.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { AdjustmentMethod, AdjustmentMethodResponse, AdjustmentOptions } from "./AdjustmentMethod";
|
||||||
|
import { WateringData } from "../../types";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only delays watering if it is currently raining and does not adjust the watering scale.
|
||||||
|
*/
|
||||||
|
async function calculateRainDelayWateringScale( adjustmentOptions: RainDelayAdjustmentOptions, wateringData: WateringData | undefined ): Promise< AdjustmentMethodResponse > {
|
||||||
|
const raining = wateringData && wateringData.raining;
|
||||||
|
const d = adjustmentOptions && adjustmentOptions.hasOwnProperty( "d" ) ? adjustmentOptions.d : 24;
|
||||||
|
return {
|
||||||
|
scale: undefined,
|
||||||
|
rawData: { raining: raining ? 1 : 0 },
|
||||||
|
rainDelay: raining ? d : undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RainDelayAdjustmentOptions extends AdjustmentOptions {
|
||||||
|
/** The rain delay to use (in hours). */
|
||||||
|
d?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const RainDelayAdjustmentMethod: AdjustmentMethod = {
|
||||||
|
calculateWateringScale: calculateRainDelayWateringScale
|
||||||
|
};
|
||||||
|
export default RainDelayAdjustmentMethod;
|
||||||
94
routes/adjustmentMethods/ZimmermanAdjustmentMethod.ts
Normal file
94
routes/adjustmentMethods/ZimmermanAdjustmentMethod.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { AdjustmentMethod, AdjustmentMethodResponse, AdjustmentOptions } from "./AdjustmentMethod";
|
||||||
|
import { WateringData } from "../../types";
|
||||||
|
import { validateValues } from "../weather";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates how much watering should be scaled based on weather and adjustment options using the Zimmerman method.
|
||||||
|
* (https://github.com/rszimm/sprinklers_pi/wiki/Weather-adjustments#formula-for-setting-the-scale)
|
||||||
|
*/
|
||||||
|
async function calculateZimmermanWateringScale( adjustmentOptions: ZimmermanAdjustmentOptions, wateringData: WateringData | undefined ): Promise< AdjustmentMethodResponse > {
|
||||||
|
|
||||||
|
// Temporarily disabled since OWM forecast data is checking if rain is forecasted for 3 hours in the future.
|
||||||
|
/*
|
||||||
|
// Don't water if it is currently raining.
|
||||||
|
if ( wateringData && wateringData.raining ) {
|
||||||
|
return {
|
||||||
|
scale: 0,
|
||||||
|
rawData: { raining: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const rawData = {
|
||||||
|
h: wateringData ? Math.round( wateringData.humidity * 100) / 100 : null,
|
||||||
|
p: wateringData ? Math.round( wateringData.precip * 100 ) / 100 : null,
|
||||||
|
t: wateringData ? Math.round( wateringData.temp * 10 ) / 10 : null,
|
||||||
|
raining: wateringData ? ( wateringData.raining ? 1 : 0 ) : null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check to make sure valid data exists for all factors
|
||||||
|
if ( !validateValues( [ "temp", "humidity", "precip" ], wateringData ) ) {
|
||||||
|
// Default to a scale of 100% if fields are missing.
|
||||||
|
return {
|
||||||
|
scale: 100,
|
||||||
|
rawData: rawData,
|
||||||
|
errorMessage: "Necessary field(s) were missing from WateringData."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let humidityBase = 30, tempBase = 70, precipBase = 0;
|
||||||
|
|
||||||
|
// Get baseline conditions for 100% water level, if provided
|
||||||
|
if ( adjustmentOptions ) {
|
||||||
|
humidityBase = adjustmentOptions.hasOwnProperty( "bh" ) ? adjustmentOptions.bh : humidityBase;
|
||||||
|
tempBase = adjustmentOptions.hasOwnProperty( "bt" ) ? adjustmentOptions.bt : tempBase;
|
||||||
|
precipBase = adjustmentOptions.hasOwnProperty( "br" ) ? adjustmentOptions.br : precipBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
let humidityFactor = ( humidityBase - wateringData.humidity ),
|
||||||
|
tempFactor = ( ( wateringData.temp - tempBase ) * 4 ),
|
||||||
|
precipFactor = ( ( precipBase - wateringData.precip ) * 200 );
|
||||||
|
|
||||||
|
// Apply adjustment options, if provided, by multiplying the percentage against the factor
|
||||||
|
if ( adjustmentOptions ) {
|
||||||
|
if ( adjustmentOptions.hasOwnProperty( "h" ) ) {
|
||||||
|
humidityFactor = humidityFactor * ( adjustmentOptions.h / 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( adjustmentOptions.hasOwnProperty( "t" ) ) {
|
||||||
|
tempFactor = tempFactor * ( adjustmentOptions.t / 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( adjustmentOptions.hasOwnProperty( "r" ) ) {
|
||||||
|
precipFactor = precipFactor * ( adjustmentOptions.r / 100 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Apply all of the weather modifying factors and clamp the result between 0 and 200%.
|
||||||
|
scale: Math.floor( Math.min( Math.max( 0, 100 + humidityFactor + tempFactor + precipFactor ), 200 ) ),
|
||||||
|
rawData: rawData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ZimmermanAdjustmentOptions extends AdjustmentOptions {
|
||||||
|
/** Base humidity (as a percentage). */
|
||||||
|
bh?: number;
|
||||||
|
/** Base temperature (in Fahrenheit). */
|
||||||
|
bt?: number;
|
||||||
|
/** Base precipitation (in inches). */
|
||||||
|
br?: number;
|
||||||
|
/** The percentage to weight the humidity factor by. */
|
||||||
|
h?: number;
|
||||||
|
/** The percentage to weight the temperature factor by. */
|
||||||
|
t?: number;
|
||||||
|
/** The percentage to weight the precipitation factor by. */
|
||||||
|
r?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ZimmermanAdjustmentMethod: AdjustmentMethod = {
|
||||||
|
calculateWateringScale: calculateZimmermanWateringScale
|
||||||
|
};
|
||||||
|
export default ZimmermanAdjustmentMethod;
|
||||||
@@ -5,8 +5,12 @@ import * as SunCalc from "suncalc";
|
|||||||
import * as moment from "moment-timezone";
|
import * as moment from "moment-timezone";
|
||||||
import * as geoTZ from "geo-tz";
|
import * as geoTZ from "geo-tz";
|
||||||
|
|
||||||
import { AdjustmentOptions, GeoCoordinates, TimeData, WateringData, WeatherData } from "../types";
|
import { GeoCoordinates, TimeData, WateringData, WeatherData } from "../types";
|
||||||
import { WeatherProvider } from "./weatherProviders/WeatherProvider";
|
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 weatherProvider: WeatherProvider = new ( require("./weatherProviders/" + ( process.env.WEATHER_PROVIDER || "OWM" ) ).default )();
|
||||||
|
|
||||||
// Define regex filters to match against location
|
// Define regex filters to match against location
|
||||||
@@ -18,11 +22,11 @@ const filters = {
|
|||||||
timezone: /^()()()()()()([+-])(\d{2})(\d{2})/
|
timezone: /^()()()()()()([+-])(\d{2})(\d{2})/
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enum of available watering scale adjustment methods.
|
/** AdjustmentMethods mapped to their numeric IDs. */
|
||||||
const ADJUSTMENT_METHOD = {
|
const ADJUSTMENT_METHOD: { [ key: number ] : AdjustmentMethod } = {
|
||||||
MANUAL: 0,
|
0: ManualAdjustmentMethod,
|
||||||
ZIMMERMAN: 1,
|
1: ZimmermanAdjustmentMethod,
|
||||||
RAIN_DELAY: 2
|
2: RainDelayAdjustmentMethod
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,52 +111,6 @@ function getTimeData( coordinates: GeoCoordinates ): TimeData {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates how much watering should be scaled based on weather and adjustment options using the Zimmerman method.
|
|
||||||
* @param adjustmentOptions Options to tweak the calculation, or undefined/null if no custom values are to be used.
|
|
||||||
* @param wateringData The weather to use to calculate watering percentage.
|
|
||||||
* @return The percentage that watering should be scaled by.
|
|
||||||
* @throws An error message will be thrown if the watering scale cannot be calculated.
|
|
||||||
*/
|
|
||||||
function calculateZimmermanWateringScale( adjustmentOptions: AdjustmentOptions, wateringData: WateringData ): number {
|
|
||||||
|
|
||||||
let humidityBase = 30, tempBase = 70, precipBase = 0;
|
|
||||||
|
|
||||||
// Check to make sure valid data exists for all factors
|
|
||||||
if ( !validateValues( [ "temp", "humidity", "precip" ], wateringData ) ) {
|
|
||||||
throw "Necessary field(s) were missing from WateringData.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get baseline conditions for 100% water level, if provided
|
|
||||||
if ( adjustmentOptions ) {
|
|
||||||
humidityBase = adjustmentOptions.hasOwnProperty( "bh" ) ? adjustmentOptions.bh : humidityBase;
|
|
||||||
tempBase = adjustmentOptions.hasOwnProperty( "bt" ) ? adjustmentOptions.bt : tempBase;
|
|
||||||
precipBase = adjustmentOptions.hasOwnProperty( "br" ) ? adjustmentOptions.br : precipBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
let humidityFactor = ( humidityBase - wateringData.humidity ),
|
|
||||||
tempFactor = ( ( wateringData.temp - tempBase ) * 4 ),
|
|
||||||
precipFactor = ( ( precipBase - wateringData.precip ) * 200 );
|
|
||||||
|
|
||||||
// Apply adjustment options, if provided, by multiplying the percentage against the factor
|
|
||||||
if ( adjustmentOptions ) {
|
|
||||||
if ( adjustmentOptions.hasOwnProperty( "h" ) ) {
|
|
||||||
humidityFactor = humidityFactor * ( adjustmentOptions.h / 100 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( adjustmentOptions.hasOwnProperty( "t" ) ) {
|
|
||||||
tempFactor = tempFactor * ( adjustmentOptions.t / 100 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( adjustmentOptions.hasOwnProperty( "r" ) ) {
|
|
||||||
precipFactor = precipFactor * ( adjustmentOptions.r / 100 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply all of the weather modifying factors and clamp the result between 0 and 200%.
|
|
||||||
return Math.floor( Math.min( Math.max( 0, 100 + humidityFactor + tempFactor + precipFactor ), 200 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the weather data meets any of the restrictions set by OpenSprinkler. Restrictions prevent any watering
|
* Checks if the weather data meets any of the restrictions set by OpenSprinkler. Restrictions prevent any watering
|
||||||
* from occurring and are similar to 0% watering level. Known restrictions are:
|
* from occurring and are similar to 0% watering level. Known restrictions are:
|
||||||
@@ -217,7 +175,7 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
// 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
|
||||||
// be saved in the same byte.
|
// be saved in the same byte.
|
||||||
let adjustmentMethod: number = req.params[ 0 ] & ~( 1 << 7 ),
|
let adjustmentMethod: AdjustmentMethod = ADJUSTMENT_METHOD[ req.params[ 0 ] & ~( 1 << 7 ) ],
|
||||||
checkRestrictions: boolean = ( ( req.params[ 0 ] >> 7 ) & 1 ) > 0,
|
checkRestrictions: boolean = ( ( req.params[ 0 ] >> 7 ) & 1 ) > 0,
|
||||||
adjustmentOptionsString: string = getParameter(req.query.wto),
|
adjustmentOptionsString: string = getParameter(req.query.wto),
|
||||||
location: string | GeoCoordinates = getParameter(req.query.loc),
|
location: string | GeoCoordinates = getParameter(req.query.loc),
|
||||||
@@ -225,13 +183,6 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
remoteAddress: string = getParameter(req.headers[ "x-forwarded-for" ]) || req.connection.remoteAddress,
|
remoteAddress: string = getParameter(req.headers[ "x-forwarded-for" ]) || req.connection.remoteAddress,
|
||||||
adjustmentOptions: AdjustmentOptions;
|
adjustmentOptions: AdjustmentOptions;
|
||||||
|
|
||||||
/* A message to include in the response to indicate that the watering scale was not calculated correctly and
|
|
||||||
defaulted to 100%. This approach is used for backwards compatibility because older OS firmware versions were
|
|
||||||
hardcoded to keep the previous watering scale if the response did not include a watering scale, but newer versions
|
|
||||||
might allow for different behaviors. */
|
|
||||||
let errorMessage: string = undefined;
|
|
||||||
|
|
||||||
|
|
||||||
// X-Forwarded-For header may contain more than one IP address and therefore
|
// X-Forwarded-For header may contain more than one IP address and therefore
|
||||||
// the string is split against a comma and the first value is selected
|
// the string is split against a comma and the first value is selected
|
||||||
remoteAddress = remoteAddress.split( "," )[ 0 ];
|
remoteAddress = remoteAddress.split( "," )[ 0 ];
|
||||||
@@ -263,7 +214,7 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
// Continue with the weather request
|
// Continue with the weather request
|
||||||
let timeData: TimeData = getTimeData( coordinates );
|
let timeData: TimeData = getTimeData( coordinates );
|
||||||
let wateringData: WateringData;
|
let wateringData: WateringData;
|
||||||
if ( adjustmentMethod !== ADJUSTMENT_METHOD.MANUAL || checkRestrictions ) {
|
if ( adjustmentMethod !== ManualAdjustmentMethod || checkRestrictions ) {
|
||||||
try {
|
try {
|
||||||
wateringData = await weatherProvider.getWateringData( coordinates );
|
wateringData = await weatherProvider.getWateringData( coordinates );
|
||||||
} catch ( err ) {
|
} catch ( err ) {
|
||||||
@@ -272,59 +223,45 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let scale = -1, rainDelay = -1;
|
let adjustmentMethodResponse: AdjustmentMethodResponse;
|
||||||
|
|
||||||
if ( adjustmentMethod === ADJUSTMENT_METHOD.ZIMMERMAN ) {
|
|
||||||
try {
|
try {
|
||||||
scale = calculateZimmermanWateringScale( adjustmentOptions, wateringData );
|
adjustmentMethodResponse = await adjustmentMethod.calculateWateringScale(
|
||||||
|
adjustmentOptions, wateringData, coordinates, weatherProvider
|
||||||
|
);
|
||||||
} catch ( err ) {
|
} catch ( err ) {
|
||||||
// Default to a scale of 100% if the scale can't be calculated.
|
if ( typeof err != "string" ) {
|
||||||
scale = 100;
|
/* If an error occurs under expected circumstances (e.g. required optional fields from a weather API are
|
||||||
errorMessage = err;
|
missing), an AdjustmentOption must throw a string. If a non-string error is caught, it is likely an Error
|
||||||
}
|
thrown by the JS engine due to unexpected circumstances. The user should not be shown the error message
|
||||||
}
|
since it may contain sensitive information. */
|
||||||
|
res.send( "Error: an unexpected error occurred." );
|
||||||
if (wateringData) {
|
console.error( `An unexpected error occurred for ${ req.url }: `, err );
|
||||||
// Check for any user-set restrictions and change the scale to 0 if the criteria is met
|
|
||||||
if (checkWeatherRestriction(req.params[0], wateringData)) {
|
|
||||||
scale = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any weather adjustment is being used, check the rain status
|
|
||||||
if ( adjustmentMethod > ADJUSTMENT_METHOD.MANUAL && wateringData.raining ) {
|
|
||||||
|
|
||||||
// If it is raining and the user has weather-based rain delay as the adjustment method then apply the specified delay
|
|
||||||
if ( adjustmentMethod === ADJUSTMENT_METHOD.RAIN_DELAY ) {
|
|
||||||
|
|
||||||
rainDelay = ( adjustmentOptions && adjustmentOptions.hasOwnProperty( "d" ) ) ? adjustmentOptions.d : 24;
|
|
||||||
} else {
|
} else {
|
||||||
// Temporarily disabled since OWM forecast data is checking if rain is forecasted for 3 hours in the future.
|
res.send( "Error: " + err );
|
||||||
// For any other adjustment method, apply a scale of 0 (as the scale will revert when the rain stops)
|
|
||||||
// scale = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale = adjustmentMethodResponse.scale;
|
||||||
|
if ( wateringData ) {
|
||||||
|
// Check for any user-set restrictions and change the scale to 0 if the criteria is met
|
||||||
|
if ( checkWeatherRestriction( req.params[ 0 ], wateringData ) ) {
|
||||||
|
scale = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
scale: scale,
|
scale: scale,
|
||||||
rd: rainDelay,
|
rd: adjustmentMethodResponse.rainDelay,
|
||||||
tz: getTimezone( timeData.timezone, undefined ),
|
tz: getTimezone( timeData.timezone, undefined ),
|
||||||
sunrise: timeData.sunrise,
|
sunrise: timeData.sunrise,
|
||||||
sunset: timeData.sunset,
|
sunset: timeData.sunset,
|
||||||
eip: ipToInt( remoteAddress ),
|
eip: ipToInt( remoteAddress ),
|
||||||
rawData: undefined,
|
rawData: adjustmentMethodResponse.rawData,
|
||||||
error: errorMessage
|
error: adjustmentMethodResponse.errorMessage
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( adjustmentMethod > ADJUSTMENT_METHOD.MANUAL ) {
|
|
||||||
data.rawData = {
|
|
||||||
h: wateringData ? Math.round( wateringData.humidity * 100) / 100 : null,
|
|
||||||
p: wateringData ? Math.round( wateringData.precip * 100 ) / 100 : null,
|
|
||||||
t: wateringData ? Math.round( wateringData.temp * 10 ) / 10 : null,
|
|
||||||
raining: wateringData ? ( wateringData.raining ? 1 : 0 ) : null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the response to the client in the requested format
|
// Return the response to the client in the requested format
|
||||||
if ( outputFormat === "json" ) {
|
if ( outputFormat === "json" ) {
|
||||||
res.json( data );
|
res.json( data );
|
||||||
@@ -336,7 +273,7 @@ export const getWateringData = async function( req: express.Request, res: expres
|
|||||||
"&sunset=" + data.sunset +
|
"&sunset=" + data.sunset +
|
||||||
"&eip=" + data.eip +
|
"&eip=" + data.eip +
|
||||||
( data.rawData ? "&rawData=" + JSON.stringify( data.rawData ) : "" ) +
|
( data.rawData ? "&rawData=" + JSON.stringify( data.rawData ) : "" ) +
|
||||||
( errorMessage ? "&error=" + encodeURIComponent( errorMessage ) : "" )
|
( data.error ? "&error=" + encodeURIComponent( data.error ) : "" )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +329,7 @@ async function httpRequest( url: string ): Promise< string > {
|
|||||||
* @param obj The object to check.
|
* @param obj The object to check.
|
||||||
* @return A boolean indicating if the object has numeric values for all of the specified keys.
|
* @return A boolean indicating if the object has numeric values for all of the specified keys.
|
||||||
*/
|
*/
|
||||||
function validateValues( keys: string[], obj: object ): boolean {
|
export function validateValues( keys: string[], obj: object ): boolean {
|
||||||
let key: string;
|
let key: string;
|
||||||
|
|
||||||
// Return false if the object is null/undefined.
|
// Return false if the object is null/undefined.
|
||||||
|
|||||||
17
types.ts
17
types.ts
@@ -68,21 +68,4 @@ export interface WateringData {
|
|||||||
raining: boolean;
|
raining: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdjustmentOptions {
|
|
||||||
/** Base humidity (as a percentage). */
|
|
||||||
bh?: number;
|
|
||||||
/** Base temperature (in Fahrenheit). */
|
|
||||||
bt?: number;
|
|
||||||
/** Base precipitation (in inches). */
|
|
||||||
br?: number;
|
|
||||||
/** The percentage to weight the humidity factor by. */
|
|
||||||
h?: number;
|
|
||||||
/** The percentage to weight the temperature factor by. */
|
|
||||||
t?: number;
|
|
||||||
/** The percentage to weight the precipitation factor by. */
|
|
||||||
r?: number;
|
|
||||||
/** The rain delay to use (in hours). */
|
|
||||||
d?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WeatherProviderId = "OWM" | "DarkSky" | "local";
|
export type WeatherProviderId = "OWM" | "DarkSky" | "local";
|
||||||
|
|||||||
Reference in New Issue
Block a user