Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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