Skip to content

Commit

Permalink
Merge pull request #2902 from SkySails/feat/smhi-weather-provider-imp…
Browse files Browse the repository at this point in the history
…rovements

feat(weather/smhi): Add hourly forecasts, apparent temperature & custom location name
  • Loading branch information
MichMich authored Aug 30, 2022
2 parents ba128cb + 50f72f0 commit ca906c4
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ _This release is scheduled to be released on 2022-10-01._
- Possibility to fetch calendars through socket notifications.
- New scripts `install-mm` (and `install-mm:dev`) for simplifying mm installation (now: `npm run install-mm`) and adding params `--no-audit --no-fund --no-update-notifier` for less noise.
- New `showTimeToday` option in calendar module shows time for current-day events even if `timeFormat` is `"relative"`
- Add hourly forecasts, apparent temperature & custom location name to SMHI weather provider

## Updated

Expand Down
61 changes: 47 additions & 14 deletions modules/default/weather/providers/smhi.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,56 @@ WeatherProvider.register("smhi", {
defaults: {
lat: 0,
lon: 0,
precipitationValue: "pmedian"
precipitationValue: "pmedian",
location: false
},

/**
* Implements method in interface for fetching current weather
* Implements method in interface for fetching current weather.
*/
fetchCurrentWeather() {
this.fetchData(this.getURL())
.then((data) => {
let closest = this.getClosestToCurrentTime(data.timeSeries);
let coordinates = this.resolveCoordinates(data);
let weatherObject = this.convertWeatherDataToObject(closest, coordinates);
this.setFetchedLocation(`(${coordinates.lat},${coordinates.lon})`);
const closest = this.getClosestToCurrentTime(data.timeSeries);
const coordinates = this.resolveCoordinates(data);
const weatherObject = this.convertWeatherDataToObject(closest, coordinates);
this.setFetchedLocation(this.config.location || `(${coordinates.lat},${coordinates.lon})`);
this.setCurrentWeather(weatherObject);
})
.catch((error) => Log.error("Could not load data: " + error.message))
.finally(() => this.updateAvailable());
},

/**
* Implements method in interface for fetching a forecast.
* Handling hourly forecast would be easy as not grouping by day but it seems really specific for one weather provider for now.
* Implements method in interface for fetching a multi-day forecast.
*/
fetchWeatherForecast() {
this.fetchData(this.getURL())
.then((data) => {
let coordinates = this.resolveCoordinates(data);
let weatherObjects = this.convertWeatherDataGroupedByDay(data.timeSeries, coordinates);
this.setFetchedLocation(`(${coordinates.lat},${coordinates.lon})`);
const coordinates = this.resolveCoordinates(data);
const weatherObjects = this.convertWeatherDataGroupedBy(data.timeSeries, coordinates);
this.setFetchedLocation(this.config.location || `(${coordinates.lat},${coordinates.lon})`);
this.setWeatherForecast(weatherObjects);
})
.catch((error) => Log.error("Could not load data: " + error.message))
.finally(() => this.updateAvailable());
},

/**
* Implements method in interface for fetching hourly forecasts.
*/
fetchWeatherHourly() {
this.fetchData(this.getURL())
.then((data) => {
const coordinates = this.resolveCoordinates(data);
const weatherObjects = this.convertWeatherDataGroupedBy(data.timeSeries, coordinates, "hour");
this.setFetchedLocation(this.config.location || `(${coordinates.lat},${coordinates.lon})`);
this.setWeatherHourly(weatherObjects);
})
.catch((error) => Log.error("Could not load data: " + error.message))
.finally(() => this.updateAvailable());
},

/**
* Overrides method for setting config with checks for the precipitationValue being unset or invalid
*
Expand Down Expand Up @@ -94,6 +109,21 @@ WeatherProvider.register("smhi", {
return `https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/${lon}/lat/${lat}/data.json`;
},

/**
* Calculates the apparent temperature based on known atmospheric data.
*
* @param {object} weatherData Weatherdata to use for the calculation
* @returns {number} The apparent temperature
*/
calculateApparentTemperature(weatherData) {
const Ta = this.paramValue(weatherData, "t");
const rh = this.paramValue(weatherData, "r");
const ws = this.paramValue(weatherData, "ws");
const p = (rh / 100) * 6.105 * Math.E * ((17.27 * Ta) / (237.7 + Ta));

return Ta + 0.33 * p - 0.7 * ws - 4;
},

/**
* Converts the returned data into a WeatherObject with required properties set for both current weather and forecast.
* The returned units is always in metric system.
Expand All @@ -114,6 +144,7 @@ WeatherProvider.register("smhi", {
currentWeather.windSpeed = this.paramValue(weatherData, "ws");
currentWeather.windDirection = this.paramValue(weatherData, "wd");
currentWeather.weatherType = this.convertWeatherType(this.paramValue(weatherData, "Wsymb2"), currentWeather.isDayTime());
currentWeather.feelsLikeTemp = this.calculateAT(weatherData);

// Determine the precipitation amount and category and update the
// weatherObject with it, the valuetype to use can be configured or uses
Expand Down Expand Up @@ -147,20 +178,22 @@ WeatherProvider.register("smhi", {
*
* @param {object[]} allWeatherData Array of weatherdata
* @param {object} coordinates Coordinates of the locations of the weather
* @param {string} groupBy The interval to use for grouping the data (day, hour)
* @returns {WeatherObject[]} Array of weatherobjects
*/
convertWeatherDataGroupedByDay(allWeatherData, coordinates) {
convertWeatherDataGroupedBy(allWeatherData, coordinates, groupBy = "day") {
let currentWeather;
let result = [];

let allWeatherObjects = this.fillInGaps(allWeatherData).map((weatherData) => this.convertWeatherDataToObject(weatherData, coordinates));
let dayWeatherTypes = [];

for (const weatherObject of allWeatherObjects) {
//If its the first object or if a day change we need to reset the summary object
if (!currentWeather || !currentWeather.date.isSame(weatherObject.date, "day")) {
//If its the first object or if a day/hour change we need to reset the summary object
if (!currentWeather || !currentWeather.date.isSame(weatherObject.date, groupBy)) {
currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
dayWeatherTypes = [];
currentWeather.temperature = weatherObject.temperature;
currentWeather.date = weatherObject.date;
currentWeather.minTemperature = Infinity;
currentWeather.maxTemperature = -Infinity;
Expand Down

0 comments on commit ca906c4

Please sign in to comment.