diff --git a/CHANGELOG.md b/CHANGELOG.md index 08894b20..437f75bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ _This release is scheduled to be released on 2023-04-01._ - Added increments for hourly forecasts in weather module (#2996) - Added tests for hourly weather forecast - Added possibility to ignore MagicMirror repo in updatenotification module +- Added Pirate Weather as new weather provider (#3005) ### Removed diff --git a/modules/default/weather/providers/pirateweather.js b/modules/default/weather/providers/pirateweather.js new file mode 100644 index 00000000..431458a7 --- /dev/null +++ b/modules/default/weather/providers/pirateweather.js @@ -0,0 +1,138 @@ +/* global WeatherProvider, WeatherObject */ + +/* MagicMirror² + * Module: Weather + * Provider: Pirate Weather + * + * Written by Nicholas Hubbard https://github.com/nhubbard for formerly Dark Sky Provider + * Modified by Karsten Hassel for Pirate Weather + * MIT Licensed + * + * This class is a provider for Pirate Weather, it is a replacement for Dark Sky (same api). + */ +WeatherProvider.register("pirateweather", { + // Set the name of the provider. + // Not strictly required, but helps for debugging. + providerName: "pirateweather", + + // Set the default config properties that is specific to this provider + defaults: { + useCorsProxy: true, + apiBase: "https://api.pirateweather.net", + weatherEndpoint: "/forecast", + apiKey: "", + lat: 0, + lon: 0 + }, + + fetchCurrentWeather() { + this.fetchData(this.getUrl()) + .then((data) => { + if (!data || !data.currently || typeof data.currently.temperature === "undefined") { + // No usable data? + return; + } + + const currentWeather = this.generateWeatherDayFromCurrentWeather(data); + this.setCurrentWeather(currentWeather); + }) + .catch(function (request) { + Log.error("Could not load data ... ", request); + }) + .finally(() => this.updateAvailable()); + }, + + fetchWeatherForecast() { + this.fetchData(this.getUrl()) + .then((data) => { + if (!data || !data.daily || !data.daily.data.length) { + // No usable data? + return; + } + + const forecast = this.generateWeatherObjectsFromForecast(data.daily.data); + this.setWeatherForecast(forecast); + }) + .catch(function (request) { + Log.error("Could not load data ... ", request); + }) + .finally(() => this.updateAvailable()); + }, + + // Create a URL from the config and base URL. + getUrl() { + return `${this.config.apiBase}${this.config.weatherEndpoint}/${this.config.apiKey}/${this.config.lat},${this.config.lon}?units=si&lang=${this.config.lang}`; + }, + + // Implement WeatherDay generator. + generateWeatherDayFromCurrentWeather(currentWeatherData) { + const currentWeather = new WeatherObject(); + + currentWeather.date = moment(); + currentWeather.humidity = parseFloat(currentWeatherData.currently.humidity); + currentWeather.temperature = parseFloat(currentWeatherData.currently.temperature); + currentWeather.windSpeed = parseFloat(currentWeatherData.currently.windSpeed); + currentWeather.windDirection = currentWeatherData.currently.windBearing; + currentWeather.weatherType = this.convertWeatherType(currentWeatherData.currently.icon); + currentWeather.sunrise = moment.unix(currentWeatherData.daily.data[0].sunriseTime); + currentWeather.sunset = moment.unix(currentWeatherData.daily.data[0].sunsetTime); + + return currentWeather; + }, + + generateWeatherObjectsFromForecast(forecasts) { + const days = []; + + for (const forecast of forecasts) { + const weather = new WeatherObject(); + + weather.date = moment.unix(forecast.time); + weather.minTemperature = forecast.temperatureMin; + weather.maxTemperature = forecast.temperatureMax; + weather.weatherType = this.convertWeatherType(forecast.icon); + weather.snow = 0; + weather.rain = 0; + + let precip = 0; + // The API will return centimeters if units is 'si' and will return inches for 'us' + if (forecast.hasOwnProperty("precipAccumulation")) { + if (this.config.units === "imperial" && !isNaN(forecast.precipAccumulation)) { + precip = forecast.precipAccumulation; + } else if (!isNaN(forecast.precipAccumulation)) { + precip = forecast.precipAccumulation * 10; + } + } + + weather.precipitation = precip; + if (forecast.hasOwnProperty("precipType")) { + if (forecast.precipType === "snow") { + weather.snow = precip; + } else { + weather.rain = precip; + } + } + + days.push(weather); + } + + return days; + }, + + // Map icons from Pirate Weather to our icons. + convertWeatherType(weatherType) { + const weatherTypes = { + "clear-day": "day-sunny", + "clear-night": "night-clear", + rain: "rain", + snow: "snow", + sleet: "snow", + wind: "wind", + fog: "fog", + cloudy: "cloudy", + "partly-cloudy-day": "day-cloudy", + "partly-cloudy-night": "night-cloudy" + }; + + return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null; + } +});