Allow temp and wind units to be specified separately if required.

This commit is contained in:
Malcolm Oakes 2019-06-07 15:27:08 +01:00
parent 2970568eab
commit a619fc4fef
9 changed files with 48 additions and 37 deletions

6
CHANGELOG.md Normal file → Executable file
View File

@ -14,10 +14,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added ### Added
Added UK Met Office Datapoint feed as a provider in the default weather module. Added UK Met Office Datapoint feed as a provider in the default weather module.
- added new provider class - added new provider class
- added suncalc.js dependency to calculate sun times (not provided in datapoint feed) - added suncalc.js dependency to calculate sun times (not provided in UK Met Office feed)
- added "ukunits": temp in degrees C, wind speed in MPH, precipitation in % - added "tempUnits" and "windUnits" to allow, for example, temp in metric (i.e. celsius) and wind in imperial (i.e. mph). These will override "units" if specified, otherwise the "units" value will be used.
- use Feels Like temp from feed if present - use Feels Like temp from feed if present
- optionally display precipitation in current weather - optionally display probability of precipitation (PoP) in current weather (UK Met Office data)
### Updated ### Updated

View File

@ -1,6 +1,6 @@
# Weather Module # Weather Module
This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes. This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
The biggest change is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source. The biggest change is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source.
@ -37,7 +37,9 @@ The following properties can be configured:
| ---------------------------- | ----------- | ---------------------------- | -----------
| `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` , `darksky` , `weathergov` or `ukmetoffice`<br> **Default value:** `openweathermap` | `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` , `darksky` , `weathergov` or `ukmetoffice`<br> **Default value:** `openweathermap`
| `type` | Which type of weather data should be displayed. <br><br> **Possible values:** `current` or `forecast` <br> **Default value:** `current` | `type` | Which type of weather data should be displayed. <br><br> **Possible values:** `current` or `forecast` <br> **Default value:** `current`
| `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit, `ukunits` = Celsius (wind speed mph) <br> **Default value:** `config.units` | `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `config.units`
| `tempUnits` | What units to use for temperature. If specified overrides `units` setting. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `units`
| `windUnits` | What units to use for wind speed. If specified overrides `units` setting. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `units`
| `roundTemp` | Round temperature value to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false` | `roundTemp` | Round temperature value to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false`
| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvin = K). <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false` | `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvin = K). <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `updateInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes) | `updateInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes)

View File

@ -18,9 +18,9 @@ This is the script in which the weather provider will be defined. In it's most s
````javascript ````javascript
WeatherProvider.register("yourprovider", { WeatherProvider.register("yourprovider", {
providerName: "YourProvider", providerName: "YourProvider",
fetchCurrentWeather() {}, fetchCurrentWeather() {},
fetchWeatherForecast() {} fetchWeatherForecast() {}
}); });
```` ````
@ -91,9 +91,11 @@ A convenience function to make requests. It returns a promise.
| Property | Type | Value/Unit | | Property | Type | Value/Unit |
| --- | --- | --- | | --- | --- | --- |
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` and `ukunits` | | units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
| tempUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
| windUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. | | date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
| windSpeed |`number` | Metric: `meter/second` <br> Imperial: `miles/hour` <br> UKunits: `miles/hour` | | windSpeed |`number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
| windDirection |`number` | Direction of the wind in degrees. | | windDirection |`number` | Direction of the wind in degrees. |
| sunrise |`object` | [Moment.js](https://momentjs.com/) object of sunrise. | | sunrise |`object` | [Moment.js](https://momentjs.com/) object of sunrise. |
| sunset |`object` | [Moment.js](https://momentjs.com/) object of sunset. | | sunset |`object` | [Moment.js](https://momentjs.com/) object of sunset. |
@ -104,7 +106,7 @@ A convenience function to make requests. It returns a promise.
| humidity | `number` | Percentage of humidity | | humidity | `number` | Percentage of humidity |
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` | | rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` | | snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> Ukunits: `percent` | | precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
#### Current weather #### Current weather

4
modules/default/weather/providers/darksky.js Normal file → Executable file
View File

@ -58,7 +58,7 @@ WeatherProvider.register("darksky", {
// Implement WeatherDay generator. // Implement WeatherDay generator.
generateWeatherDayFromCurrentWeather(currentWeatherData) { generateWeatherDayFromCurrentWeather(currentWeatherData) {
const currentWeather = new WeatherObject(this.config.units); const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
currentWeather.date = moment(); currentWeather.date = moment();
currentWeather.humidity = parseFloat(currentWeatherData.currently.humidity); currentWeather.humidity = parseFloat(currentWeatherData.currently.humidity);
@ -76,7 +76,7 @@ WeatherProvider.register("darksky", {
const days = []; const days = [];
for (const forecast of forecasts) { for (const forecast of forecasts) {
const weather = new WeatherObject(this.config.units); const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
weather.date = moment(forecast.time, "X"); weather.date = moment(forecast.time, "X");
weather.minTemperature = forecast.temperatureMin; weather.minTemperature = forecast.temperatureMin;

12
modules/default/weather/providers/openweathermap.js Normal file → Executable file
View File

@ -68,7 +68,7 @@ WeatherProvider.register("openweathermap", {
* Generate a WeatherObject based on currentWeatherInformation * Generate a WeatherObject based on currentWeatherInformation
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather(currentWeatherData) {
const currentWeather = new WeatherObject(this.config.units); const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
currentWeather.humidity = currentWeatherData.main.humidity; currentWeather.humidity = currentWeatherData.main.humidity;
currentWeather.temperature = currentWeatherData.main.temp; currentWeather.temperature = currentWeatherData.main.temp;
@ -92,7 +92,7 @@ WeatherProvider.register("openweathermap", {
return this.fetchForecastDaily(forecasts); return this.fetchForecastDaily(forecasts);
} }
// if weatherEndpoint does not match forecast or forecast/daily, what should be returned? // if weatherEndpoint does not match forecast or forecast/daily, what should be returned?
const days = [new WeatherObject(this.config.units)]; const days = [new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits)];
return days; return days;
}, },
@ -109,7 +109,7 @@ WeatherProvider.register("openweathermap", {
let snow = 0; let snow = 0;
// variable for date // variable for date
let date = ""; let date = "";
let weather = new WeatherObject(this.config.units); let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
for (const forecast of forecasts) { for (const forecast of forecasts) {
@ -123,7 +123,7 @@ WeatherProvider.register("openweathermap", {
// push weather information to days array // push weather information to days array
days.push(weather); days.push(weather);
// create new weather-object // create new weather-object
weather = new WeatherObject(this.config.units); weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
minTemp = []; minTemp = [];
maxTemp = []; maxTemp = [];
@ -140,7 +140,7 @@ WeatherProvider.register("openweathermap", {
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon); weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
} }
if (moment(forecast.dt, "X").format("H") >= 8 && moment(forecast.dt, "X").format("H") <= 17) { if (moment(forecast.dt, "X").format("H") >= 8 && moment(forecast.dt, "X").format("H") <= 17) {
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon); weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
} }
@ -187,7 +187,7 @@ WeatherProvider.register("openweathermap", {
const days = []; const days = [];
for (const forecast of forecasts) { for (const forecast of forecasts) {
const weather = new WeatherObject(this.config.units); const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
weather.date = moment(forecast.dt, "X"); weather.date = moment(forecast.dt, "X");
weather.minTemperature = forecast.temp.min; weather.minTemperature = forecast.temp.min;

View File

@ -19,8 +19,7 @@ WeatherProvider.register("ukmetoffice", {
units: { units: {
imperial: "us", imperial: "us",
metric: "si", metric: "si"
ukunits: "uk"
}, },
// Overwrite the fetchCurrentWeather method. // Overwrite the fetchCurrentWeather method.
@ -79,7 +78,7 @@ WeatherProvider.register("ukmetoffice", {
* Generate a WeatherObject based on currentWeatherInformation * Generate a WeatherObject based on currentWeatherInformation
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather(currentWeatherData) {
const currentWeather = new WeatherObject(this.config.units); const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
// data times are always UTC // data times are always UTC
let nowUtc = moment.utc() let nowUtc = moment.utc()
@ -132,7 +131,7 @@ WeatherProvider.register("ukmetoffice", {
// loop round the (5) periods getting the data // loop round the (5) periods getting the data
// for each period array, Day is [0], Night is [1] // for each period array, Day is [0], Night is [1]
for (j in forecasts.SiteRep.DV.Location.Period) { for (j in forecasts.SiteRep.DV.Location.Period) {
const weather = new WeatherObject(this.config.units); const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
// data times are always UTC // data times are always UTC
dateStr = forecasts.SiteRep.DV.Location.Period[j].value dateStr = forecasts.SiteRep.DV.Location.Period[j].value
@ -212,14 +211,14 @@ WeatherProvider.register("ukmetoffice", {
* Convert temp (from degrees C) if required * Convert temp (from degrees C) if required
*/ */
convertTemp(tempInC) { convertTemp(tempInC) {
return this.units === "imperial" ? tempInC * 9 / 5 + 32 : tempInC; return this.tempUnits === "imperial" ? tempInC * 9 / 5 + 32 : tempInC;
}, },
/* /*
* Convert wind speed (from mph) if required * Convert wind speed (from mph) if required
*/ */
convertWindSpeed(windInMph) { convertWindSpeed(windInMph) {
return this.units === "metric" ? windInMph * 2.23694 : windInMph; return this.windUnits === "metric" ? windInMph * 2.23694 : windInMph;
}, },
/* /*

10
modules/default/weather/providers/weathergov.js Normal file → Executable file
View File

@ -67,7 +67,7 @@ WeatherProvider.register("weathergov", {
* Generate a WeatherObject based on currentWeatherInformation * Generate a WeatherObject based on currentWeatherInformation
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather(currentWeatherData) {
const currentWeather = new WeatherObject(this.config.units); const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
currentWeather.temperature = currentWeatherData.temperature; currentWeather.temperature = currentWeatherData.temperature;
currentWeather.windSpeed = currentWeatherData.windSpeed.split(" ", 1); currentWeather.windSpeed = currentWeatherData.windSpeed.split(" ", 1);
@ -95,7 +95,7 @@ WeatherProvider.register("weathergov", {
let maxTemp = []; let maxTemp = [];
// variable for date // variable for date
let date = ""; let date = "";
let weather = new WeatherObject(this.config.units); let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
weather.precipitation = 0; weather.precipitation = 0;
for (const forecast of forecasts) { for (const forecast of forecasts) {
@ -109,7 +109,7 @@ WeatherProvider.register("weathergov", {
// push weather information to days array // push weather information to days array
days.push(weather); days.push(weather);
// create new weather-object // create new weather-object
weather = new WeatherObject(this.config.units); weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
minTemp = []; minTemp = [];
maxTemp = []; maxTemp = [];
@ -134,7 +134,7 @@ WeatherProvider.register("weathergov", {
minTemp.push(forecast.temperature); minTemp.push(forecast.temperature);
maxTemp.push(forecast.temperature); maxTemp.push(forecast.temperature);
} }
// last day // last day
// calculate minimum/maximum temperature, specify rain amount // calculate minimum/maximum temperature, specify rain amount
weather.minTemperature = Math.min.apply(null, minTemp); weather.minTemperature = Math.min.apply(null, minTemp);
@ -202,7 +202,7 @@ WeatherProvider.register("weathergov", {
} }
return "night-clear"; return "night-clear";
} else if (weatherType.includes("Dust") || weatherType.includes("Sand")) { } else if (weatherType.includes("Dust") || weatherType.includes("Sand")) {
return "dust"; return "dust";
} else if (weatherType.includes("Fog")) { } else if (weatherType.includes("Fog")) {
return "fog"; return "fog";

View File

@ -19,6 +19,10 @@ Module.register("weather",{
locationID: false, locationID: false,
appid: "", appid: "",
units: config.units, units: config.units,
tempUnits: config.units,
windUnits: config.units,
updateInterval: 10 * 60 * 1000, // every 10 minutes updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000, animationSpeed: 1000,
timeFormat: config.timeFormat, timeFormat: config.timeFormat,
@ -85,6 +89,7 @@ Module.register("weather",{
// Start the weather module. // Start the weather module.
start: function () { start: function () {
moment.locale(this.config.lang); moment.locale(this.config.lang);
// Initialize the weather provider. // Initialize the weather provider.
this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this); this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this);
@ -189,13 +194,13 @@ Module.register("weather",{
this.nunjucksEnvironment().addFilter("unit", function (value, type) { this.nunjucksEnvironment().addFilter("unit", function (value, type) {
if (type === "temperature") { if (type === "temperature") {
if (this.config.units === "metric" || this.config.units === "imperial" || this.config.units === "ukunits") { if (this.config.tempUnits === "metric" || this.config.tempUnits === "imperial") {
value += "°"; value += "°";
} }
if (this.config.degreeLabel) { if (this.config.degreeLabel) {
if (this.config.units === "metric" || this.config.units === "ukunits") { if (this.config.tempUnits === "metric") {
value += "C"; value += "C";
} else if (this.config.units === "imperial") { } else if (this.config.tempUnits === "imperial") {
value += "F"; value += "F";
} else { } else {
value += "K"; value += "K";

View File

@ -13,8 +13,11 @@
// As soon as we start implementing the forecast, mode properties will be added. // As soon as we start implementing the forecast, mode properties will be added.
class WeatherObject { class WeatherObject {
constructor(units) { constructor(units, tempUnits, windUnits) {
this.units = units; this.units = units;
this.tempUnits = tempUnits;
this.windUnits = windUnits;
this.date = null; this.date = null;
this.windSpeed = null; this.windSpeed = null;
this.windDirection = null; this.windDirection = null;
@ -69,7 +72,7 @@ class WeatherObject {
} }
beaufortWindSpeed() { beaufortWindSpeed() {
const windInKmh = (this.units === "imperial" || this.units === "ukunits") ? this.windSpeed * 1.609344 : this.windSpeed * 60 * 60 / 1000; const windInKmh = (this.windUnits === "imperial") ? this.windSpeed * 1.609344 : this.windSpeed * 60 * 60 / 1000;
const speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000]; const speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
for (const [index, speed] of speeds.entries()) { for (const [index, speed] of speeds.entries()) {
if (speed > windInKmh) { if (speed > windInKmh) {
@ -87,8 +90,8 @@ class WeatherObject {
if (this.feelsLikeTemp) { if (this.feelsLikeTemp) {
return this.feelsLikeTemp return this.feelsLikeTemp
} }
const windInMph = (this.units === "imperial" || this.units === "ukunits") ? this.windSpeed : this.windSpeed * 2.23694; const windInMph = (this.windUnits === "imperial") ? this.windSpeed : this.windSpeed * 2.23694;
const tempInF = this.units === "imperial" ? this.temperature : this.temperature * 9 / 5 + 32; const tempInF = this.tempUnits === "imperial" ? this.temperature : this.temperature * 9 / 5 + 32;
let feelsLike = tempInF; let feelsLike = tempInF;
if (windInMph > 3 && tempInF < 50) { if (windInMph > 3 && tempInF < 50) {
@ -102,6 +105,6 @@ class WeatherObject {
- 1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity; - 1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
} }
return this.units === "imperial" ? feelsLike : (feelsLike - 32) * 5 / 9; return this.tempUnits === "imperial" ? feelsLike : (feelsLike - 32) * 5 / 9;
} }
} }