Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(weather/smhi): Add hourly forecasts, apparent temperature & custom location name #2902

Merged
merged 6 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
47 changes: 37 additions & 10 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})`);
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})`);
let 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) => {
let coordinates = this.resolveCoordinates(data);
SkySails marked this conversation as resolved.
Show resolved Hide resolved
let weatherObjects = this.convertWeatherDataGroupedBy(data.timeSeries, coordinates, "hour");
SkySails marked this conversation as resolved.
Show resolved Hide resolved
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,16 @@ 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. */
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 +139,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 @@ -149,18 +175,19 @@ WeatherProvider.register("smhi", {
* @param {object} coordinates Coordinates of the locations of the weather
* @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