Merge 'dev' into 'eto'
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import * as moment from "moment-timezone";
|
||||
|
||||
import { GeoCoordinates, WateringData, WeatherData } from "../../types";
|
||||
import { GeoCoordinates, WeatherData, ZimmermanWateringData } from "../../types";
|
||||
import { httpJSONRequest } from "../weather";
|
||||
import { WeatherProvider } from "./WeatherProvider";
|
||||
import { approximateSolarRadiation, CloudCoverInfo, EToData } from "../adjustmentMethods/EToAdjustmentMethod";
|
||||
|
||||
export default class DarkSkyWeatherProvider extends WeatherProvider {
|
||||
|
||||
public async getWateringData( coordinates: GeoCoordinates ): Promise< WateringData > {
|
||||
public async getWateringData( coordinates: GeoCoordinates ): Promise< ZimmermanWateringData > {
|
||||
// The Unix timestamp of 24 hours ago.
|
||||
const yesterdayTimestamp: number = moment().subtract( 1, "day" ).unix();
|
||||
const todayTimestamp: number = moment().unix();
|
||||
@@ -48,9 +48,16 @@ export default class DarkSkyWeatherProvider extends WeatherProvider {
|
||||
|
||||
const totals = { temp: 0, humidity: 0, precip: 0 };
|
||||
for ( const sample of samples ) {
|
||||
/*
|
||||
* If temperature or humidity is missing from a sample, the total will become NaN. This is intended since
|
||||
* calculateWateringScale will treat NaN as a missing value and temperature/humidity can't be accurately
|
||||
* calculated when data is missing from some samples (since they follow diurnal cycles and will be
|
||||
* significantly skewed if data is missing for several consecutive hours).
|
||||
*/
|
||||
totals.temp += sample.temperature;
|
||||
totals.humidity += sample.humidity;
|
||||
totals.precip += sample.precipIntensity
|
||||
// This field may be missing from the response if it is snowing.
|
||||
totals.precip += sample.precipIntensity || 0;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GeoCoordinates, WateringData, WeatherData } from "../../types";
|
||||
import { GeoCoordinates, WeatherData, ZimmermanWateringData } from "../../types";
|
||||
import { httpJSONRequest } from "../weather";
|
||||
import { WeatherProvider } from "./WeatherProvider";
|
||||
import { approximateSolarRadiation, CloudCoverInfo, EToData } from "../adjustmentMethods/EToAdjustmentMethod";
|
||||
@@ -6,7 +6,7 @@ import * as moment from "moment";
|
||||
|
||||
export default class OWMWeatherProvider extends WeatherProvider {
|
||||
|
||||
public async getWateringData( coordinates: GeoCoordinates ): Promise< WateringData > {
|
||||
public async getWateringData( coordinates: GeoCoordinates ): Promise< ZimmermanWateringData > {
|
||||
const OWM_API_KEY = process.env.OWM_API_KEY,
|
||||
forecastUrl = "http://api.openweathermap.org/data/2.5/forecast?appid=" + OWM_API_KEY + "&units=imperial&lat=" + coordinates[ 0 ] + "&lon=" + coordinates[ 1 ];
|
||||
|
||||
|
||||
44
routes/weatherProviders/WUnderground.ts
Normal file
44
routes/weatherProviders/WUnderground.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { GeoCoordinates, PWS, WeatherData, ZimmermanWateringData } from "../../types";
|
||||
import { WeatherProvider } from "./WeatherProvider";
|
||||
import { httpJSONRequest } from "../weather";
|
||||
|
||||
export default class WUnderground extends WeatherProvider {
|
||||
|
||||
async getWateringData( coordinates: GeoCoordinates, pws?: PWS ): Promise< ZimmermanWateringData > {
|
||||
if ( !pws ) {
|
||||
throw "WUnderground WeatherProvider requires a PWS to be specified.";
|
||||
}
|
||||
|
||||
const url = `https://api.weather.com/v2/pws/observations/hourly/7day?stationId=${ pws.id }&format=json&units=e&apiKey=${ pws.apiKey }`;
|
||||
let data;
|
||||
try {
|
||||
data = await httpJSONRequest( url );
|
||||
} catch ( err ) {
|
||||
console.error( "Error retrieving weather information from WUnderground:", err );
|
||||
throw "An error occurred while retrieving weather information from WUnderground."
|
||||
}
|
||||
|
||||
// Take the 24 most recent observations.
|
||||
const samples = data.observations.slice( -24 );
|
||||
|
||||
// Fail if not enough data is available.
|
||||
if ( samples.length !== 24 ) {
|
||||
throw "Insufficient data was returned by WUnderground.";
|
||||
}
|
||||
|
||||
const totals = { temp: 0, humidity: 0, precip: 0 };
|
||||
for ( const sample of samples ) {
|
||||
totals.temp += sample.imperial.tempAvg;
|
||||
totals.humidity += sample.humidityAvg;
|
||||
totals.precip += sample.imperial.precipRate;
|
||||
}
|
||||
|
||||
return {
|
||||
weatherProvider: "WUnderground",
|
||||
temp: totals.temp / samples.length,
|
||||
humidity: totals.humidity / samples.length,
|
||||
precip: totals.precip,
|
||||
raining: samples[ samples.length - 1 ].imperial.precipRate > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
import { GeoCoordinates, WateringData, WeatherData } from "../../types";
|
||||
import { GeoCoordinates, PWS, WeatherData, ZimmermanWateringData } from "../../types";
|
||||
import { EToData } from "../adjustmentMethods/EToAdjustmentMethod";
|
||||
|
||||
export class WeatherProvider {
|
||||
/**
|
||||
* Retrieves weather data necessary for watering level calculations.
|
||||
* Retrieves weather data necessary for Zimmerman watering level calculations.
|
||||
* @param coordinates The coordinates to retrieve the watering data for.
|
||||
* @return A Promise that will be resolved with the WateringData if it is successfully retrieved,
|
||||
* or rejected with an error message if an error occurs while retrieving the WateringData or the WeatherProvider
|
||||
* @param pws The PWS to retrieve the weather from, or undefined if a PWS should not be used. If the implementation
|
||||
* of this method does not have PWS support, this parameter may be ignored and coordinates may be used instead.
|
||||
* @return A Promise that will be resolved with the ZimmermanWateringData if it is successfully retrieved,
|
||||
* or rejected with an error message if an error occurs while retrieving the ZimmermanWateringData or the WeatherProvider
|
||||
* does not support this method.
|
||||
*/
|
||||
getWateringData( coordinates : GeoCoordinates ): Promise< WateringData > {
|
||||
getWateringData( coordinates: GeoCoordinates, pws?: PWS ): Promise< ZimmermanWateringData > {
|
||||
throw "Selected WeatherProvider does not support getWateringData";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as express from "express";
|
||||
import { CronJob } from "cron";
|
||||
import { GeoCoordinates, WateringData } from "../../types";
|
||||
import { GeoCoordinates, ZimmermanWateringData } from "../../types";
|
||||
import { WeatherProvider } from "./WeatherProvider";
|
||||
|
||||
const count = { temp: 0, humidity: 0 };
|
||||
@@ -46,9 +46,9 @@ export const captureWUStream = function( req: express.Request, res: express.Resp
|
||||
|
||||
export default class LocalWeatherProvider extends WeatherProvider {
|
||||
|
||||
public async getWateringData( coordinates: GeoCoordinates ): Promise< WateringData > {
|
||||
const result: WateringData = {
|
||||
...yesterday as WateringData,
|
||||
public async getWateringData( coordinates: GeoCoordinates ): Promise< ZimmermanWateringData > {
|
||||
const result: ZimmermanWateringData = {
|
||||
...yesterday as ZimmermanWateringData,
|
||||
// Use today's weather if we dont have information for yesterday yet (i.e. on startup)
|
||||
...today,
|
||||
// PWS report "buckets" so consider it still raining if last bucket was less than an hour ago
|
||||
|
||||
Reference in New Issue
Block a user