|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /* global WeatherProvider, WeatherObject */
-
- /* Magic Mirror
- * Module: Weather
- *
- * By Malcolm Oakes https://github.com/maloakes
- * MIT Licensed.
- *
- * This class is a provider for UK Met Office Datapoint.
- */
- WeatherProvider.register("ukmetoffice", {
- // Set the name of the provider.
- // This isn't strictly necessary, since it will fallback to the provider identifier
- // But for debugging (and future alerts) it would be nice to have the real name.
- providerName: "UK Met Office",
-
- // Set the default config properties that is specific to this provider
- defaults: {
- apiBase: "http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/",
- locationID: false,
- apiKey: ""
- },
-
- units: {
- imperial: "us",
- metric: "si"
- },
-
- // Overwrite the fetchCurrentWeather method.
- fetchCurrentWeather() {
- this.fetchData(this.getUrl("3hourly"))
- .then((data) => {
- if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
- // Did not receive usable new data.
- // Maybe this needs a better check?
- return;
- }
-
- this.setFetchedLocation(`${data.SiteRep.DV.Location.name}, ${data.SiteRep.DV.Location.country}`);
-
- const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
- this.setCurrentWeather(currentWeather);
- })
- .catch(function (request) {
- Log.error("Could not load data ... ", request);
- })
- .finally(() => this.updateAvailable());
- },
-
- // Overwrite the fetchCurrentWeather method.
- fetchWeatherForecast() {
- this.fetchData(this.getUrl("daily"))
- .then((data) => {
- if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
- // Did not receive usable new data.
- // Maybe this needs a better check?
- return;
- }
-
- this.setFetchedLocation(`${data.SiteRep.DV.Location.name}, ${data.SiteRep.DV.Location.country}`);
-
- const forecast = this.generateWeatherObjectsFromForecast(data);
- this.setWeatherForecast(forecast);
- })
- .catch(function (request) {
- Log.error("Could not load data ... ", request);
- })
- .finally(() => this.updateAvailable());
- },
-
- /** UK Met Office Specific Methods - These are not part of the default provider methods */
- /*
- * Gets the complete url for the request
- */
- getUrl(forecastType) {
- return this.config.apiBase + this.config.locationID + this.getParams(forecastType);
- },
-
- /*
- * Generate a WeatherObject based on currentWeatherInformation
- */
- generateWeatherObjectFromCurrentWeather(currentWeatherData) {
- const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
- const location = currentWeatherData.SiteRep.DV.Location;
-
- // data times are always UTC
- let nowUtc = moment.utc();
- let midnightUtc = nowUtc.clone().startOf("day");
- let timeInMins = nowUtc.diff(midnightUtc, "minutes");
-
- // loop round each of the (5) periods, look for today (the first period may be yesterday)
- for (const period of location.Period) {
- const periodDate = moment.utc(period.value.substr(0, 10), "YYYY-MM-DD");
-
- // ignore if period is before today
- if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
- // check this is the period we want, after today the diff will be -ve
- if (moment().diff(periodDate, "minutes") > 0) {
- // loop round the reports looking for the one we are in
- // $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
- for (const rep of period.Rep) {
- const p = rep.$;
- if (timeInMins >= p && timeInMins - 180 < p) {
- // finally got the one we want, so populate weather object
- currentWeather.humidity = rep.H;
- currentWeather.temperature = this.convertTemp(rep.T);
- currentWeather.feelsLikeTemp = this.convertTemp(rep.F);
- currentWeather.precipitation = parseInt(rep.Pp);
- currentWeather.windSpeed = this.convertWindSpeed(rep.S);
- currentWeather.windDirection = this.convertWindDirection(rep.D);
- currentWeather.weatherType = this.convertWeatherType(rep.W);
- }
- }
- }
- }
- }
-
- // determine the sunrise/sunset times - not supplied in UK Met Office data
- currentWeather.updateSunTime(location.lat, location.lon);
-
- return currentWeather;
- },
-
- /*
- * Generate WeatherObjects based on forecast information
- */
- generateWeatherObjectsFromForecast(forecasts) {
- const days = [];
-
- // loop round the (5) periods getting the data
- // for each period array, Day is [0], Night is [1]
- for (const period of forecasts.SiteRep.DV.Location.Period) {
- const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
-
- // data times are always UTC
- const dateStr = period.value;
- let periodDate = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
-
- // ignore if period is before today
- if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
- // populate the weather object
- weather.date = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
- weather.minTemperature = this.convertTemp(period.Rep[1].Nm);
- weather.maxTemperature = this.convertTemp(period.Rep[0].Dm);
- weather.weatherType = this.convertWeatherType(period.Rep[0].W);
- weather.precipitation = parseInt(period.Rep[0].PPd);
-
- days.push(weather);
- }
- }
-
- return days;
- },
-
- /*
- * Convert the Met Office icons to a more usable name.
- */
- convertWeatherType(weatherType) {
- const weatherTypes = {
- 0: "night-clear",
- 1: "day-sunny",
- 2: "night-alt-cloudy",
- 3: "day-cloudy",
- 5: "fog",
- 6: "fog",
- 7: "cloudy",
- 8: "cloud",
- 9: "night-sprinkle",
- 10: "day-sprinkle",
- 11: "raindrops",
- 12: "sprinkle",
- 13: "night-alt-showers",
- 14: "day-showers",
- 15: "rain",
- 16: "night-alt-sleet",
- 17: "day-sleet",
- 18: "sleet",
- 19: "night-alt-hail",
- 20: "day-hail",
- 21: "hail",
- 22: "night-alt-snow",
- 23: "day-snow",
- 24: "snow",
- 25: "night-alt-snow",
- 26: "day-snow",
- 27: "snow",
- 28: "night-alt-thunderstorm",
- 29: "day-thunderstorm",
- 30: "thunderstorm"
- };
-
- return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null;
- },
-
- /*
- * Convert temp (from degrees C) if required
- */
- convertTemp(tempInC) {
- return this.tempUnits === "imperial" ? (tempInC * 9) / 5 + 32 : tempInC;
- },
-
- /*
- * Convert wind speed (from mph to m/s or km/h) if required
- */
- convertWindSpeed(windInMph) {
- return this.windUnits === "metric" ? (this.useKmh ? windInMph * 1.60934 : windInMph / 2.23694) : windInMph;
- },
-
- /*
- * Convert the wind direction cardinal to value
- */
- convertWindDirection(windDirection) {
- const windCardinals = {
- N: 0,
- NNE: 22,
- NE: 45,
- ENE: 67,
- E: 90,
- ESE: 112,
- SE: 135,
- SSE: 157,
- S: 180,
- SSW: 202,
- SW: 225,
- WSW: 247,
- W: 270,
- WNW: 292,
- NW: 315,
- NNW: 337
- };
-
- return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
- },
-
- /**
- * Generates an url with api parameters based on the config.
- *
- * @param {string} forecastType daily or 3hourly forecast
- * @returns {string} url
- */
- getParams(forecastType) {
- let params = "?";
- params += "res=" + forecastType;
- params += "&key=" + this.config.apiKey;
- return params;
- }
- });
|