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

Weather refactoring #1488

Merged
merged 35 commits into from
Dec 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ef17259
A first setup of the new Weather Module
MichMich Sep 21, 2017
7f2e643
Add Dark Sky Module
nhubbard Sep 22, 2017
2bf18d8
Fix Indentation?
nhubbard Sep 22, 2017
5b1462a
Add readme.
MichMich Sep 22, 2017
7131112
First implementation of the currentWeatherView
MichMich Sep 22, 2017
ff9c6ba
Add a small forecast example.
MichMich Sep 22, 2017
7be6031
Merge branch 'weather-refactor' of https://github.com/MichMich/MagicM…
nhubbard Sep 29, 2017
837e275
Update fork
nhubbard Sep 29, 2017
cd129fb
Revert "Fix Indentation?"
nhubbard Sep 30, 2017
3fa810b
Merge branch 'develop' into weather-refactor
MichMich Oct 1, 2017
99e3a47
Use templates to render weather.
MichMich Oct 1, 2017
ad240cf
Fix lint errors.
MichMich Oct 1, 2017
681a845
Add Darksky provider.
MichMich Oct 3, 2017
0776dfc
Minor changes.
MichMich Oct 18, 2017
d567fd4
Merge branch 'develop' into weather-refactor
MichMich Oct 18, 2017
ec2169e
Merge branch 'develop' into weather-refactor
MichMich Oct 18, 2017
241ff5c
Set temperature rounding.
MichMich Oct 18, 2017
ab732b5
Make all visiable values dynamic.
MichMich Oct 18, 2017
995296e
Merge branch 'develop' into weather-refactor
MichMich Oct 18, 2017
a79e1b6
Rename templates to .njk files to allow syntax highlighting.
MichMich Oct 18, 2017
22a50b7
Show unit.
MichMich Oct 19, 2017
16c8878
Show humidity.
MichMich Oct 19, 2017
07d35a8
Remove todo item.
MichMich Oct 19, 2017
91ddc00
fix moment, add unit filter
fewieden May 21, 2018
3341c9e
start with forecast template
fewieden May 21, 2018
66ceafd
show indoor data, add loading message
fewieden Jun 16, 2018
0fe79b5
indoor data, new filter, small cleanup
fewieden Jul 2, 2018
6383618
weatherprovider
fewieden Dec 27, 2018
ebee80d
small improvements
fewieden Dec 27, 2018
95adc0a
forecast
fewieden Dec 27, 2018
0ed2ba0
darksky forecast and darksky current weather fixes
fewieden Dec 27, 2018
1920f81
config options and documentation
fewieden Dec 27, 2018
10bc326
cleanup
fewieden Dec 27, 2018
7a0bc81
Merge branch 'develop' of https://github.com/MichMich/MagicMirror int…
fewieden Dec 27, 2018
b853c00
Add changelog entry
fewieden Dec 27, 2018
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [2.6.0] - Unreleased

*This release is scheduled to be released on 2018-10-01.*
*This release is scheduled to be released on 2019-01-01.*

### Experimental
- New default [module weather](modules/default/weather).

### Added
- Possibility to add classes to the cell of symbol, title and time of the events of calendar.
Expand Down
3 changes: 2 additions & 1 deletion modules/default/defaultmodules.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ var defaultModules = [
"helloworld",
"newsfeed",
"weatherforecast",
"updatenotification"
"updatenotification",
"weather"
];

/*************** DO NOT EDIT THE LINE BELOW ***************/
Expand Down
94 changes: 94 additions & 0 deletions modules/default/weather/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# 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.

The biggest cange 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 module is in a very early stage, and needs a lot of work. It's API isn't set in stone, so keep that in mind when you want to contribute.

## Example

![Current Weather Screenshot](current.png) ![Weather Forecast Screenshot](forecast.png)

## Usage

To use this module, add it to the modules array in the `config/config.js` file:

````javascript
modules: [
{
module: "weather",
position: "top_right",
config: {
// See 'Configuration options' for more information.
type: 'current'
}
}
]
````

## Configuration options

The following properties can be configured:

### General options

| Option | Description
| ---------------------------- | -----------
| `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` and `darksky` <br> **Default value:** `openweathermap`
| `type` | Which type of weather data should be displayed. <br><br> **Possible values:** `current` and `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 <br> **Default value:** `config.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`
| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvins = 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)
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:**`0` - `5000` <br> **Default value:** `1000` (1 second)
| `timeFormat` | Use 12 or 24 hour format. <br><br> **Possible values:** `12` or `24` <br> **Default value:** uses value of _config.timeFormat_
| `showPeriod` | Show the period (am/pm) with 12 hour format <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
| `initialLoadDelay` | The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds) <br><br> **Possible values:** `1000` - `5000` <br> **Default value:** `0`
| `retryDelay` | The delay before retrying after a request failure. (Milliseconds) <br><br> **Possible values:** `1000` - `60000` <br> **Default value:** `2500`
| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather. <br><br> **Default value:** `true`
| `calendarClass` | The class for the calender module to base the event based weather information on. <br><br> **Default value:** `'calendar'`

#### Current weather options

| Option | Description
| ---------------------------- | -----------
| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset, sunrise time and feels like. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `showWindDirection` | Show the wind direction next to the wind speed. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `showWindDirectionAsArrow` | Show the wind direction as an arrow instead of abbreviation <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `showHumidity` | Show the current humidity <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `showIndoorTemperature` | If you have another module that emits the `INDOOR_TEMPERATURE` notification, the indoor temperature will be displayed <br> **Default value:** `false`
| `showIndoorHumidity` | If you have another module that emits the `INDOOR_HUMIDITY` notification, the indoor humidity will be displayed <br> **Default value:** `false`

#### Weather forecast options

| Option | Description
| ---------------------------- | -----------
| `tableClass` | The class for the forecast table. <br><br> **Default value:** `'small'`
| `colored` | If set to `true`, the min and max temperature are color coded. <br><br> **Default value:** `false`
| `showRainAmount` | Show the amount of rain in the forecast <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`

### Openweathermap options

| Option | Description
| ---------------------------- | -----------
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
| `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **Possible values:** `/weather` or `/forecast/daily` <br> **Default value:** `'/weather'`
| `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
| `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account. <br><br> This value is **REQUIRED**

### Darksky options

| Option | Description
| ---------------------------- | -----------
| `apiBase` | The DarkSky base URL. The darksky api has disabled [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), therefore a proxy is required. <br><br> **Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'` <br> This value is **REQUIRED**
| `weatherEndpoint` | The DarkSky API endPoint. <br><br> **Possible values:** `/forecast` <br> This value is **REQUIRED**
| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account. <br><br> This value is **REQUIRED**
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
61 changes: 61 additions & 0 deletions modules/default/weather/current.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{% if current %}
{% if not config.onlyTemp %}
<div class="normal medium">
<span class="wi wi-strong-wind dimmed"></span>
<span>
{% if config.useBeaufort %}
{{current.beaufortWindSpeed() | round}}
{% else %}
{{current.windSpeed | round}}
{% endif %}

{% if config.showWindDirection %}
<sup>
{% if config.showWindDirectionAsArrow %}
<i class="fa fa-long-arrow-up" style="transform:rotate({{current.windDirection}}deg);"></i>
{% else %}
{{current.cardinalWindDirection() | translate}}
{% endif %}
&nbsp;
</sup>
{% endif %}
</span>
{% if config.showHumidity and current.humidity %}
<span>{{ current.humidity }}</span><sup>&nbsp;<i class="wi wi-humidity humidityIcon"></i></sup>
{% endif %}
<span class="wi dimmed wi-{{current.nextSunAction()}}"></span>
<span>
{% if current.nextSunAction() == "sunset" %}
{{current.sunset | formatTime}}
{% else %}
{{current.sunrise | formatTime}}
{% endif %}
</span>
</div>
{% endif %}
<div class="large light">
<span class="wi weathericon wi-{{current.weatherType}}"></span>
<span class="bright">
{{current.temperature | roundValue | unit("temperature")}}
</span>
{% if config.showIndoorTemperature and indoor.temperature %}
<span class="fa fa-home"></span>
<span class="bright">
{{indoor.temperature | roundValue | unit("temperature")}}
</span>
{% endif %}
{% if config.showIndoorHumidity and indoor.humidity %}
<span class="fa fa-tint"></span>
<span class="bright">
{{indoor.humidity | roundValue}}%
</span>
{% endif %}
</div>
{% else %}
<div class="dimmed light small">
{{"LOADING" | translate}}
</div>
{% endif %}

<!-- Unclomment the line below to see the contents of the `current` object. -->
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{current | dump}}</div> -->
Binary file added modules/default/weather/current.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions modules/default/weather/forecast.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% if forecast %}
<table class="{{config.tableClass}}">
{% for f in forecast %}
<tr {% if config.colored %}class="colored"{% endif %}>
<td class="day">{{f.date.format('ddd')}}</td>
<td class="bright weather-icon"><span class="wi weathericon wi-{{f.weatherType}}"></span></td>
<td class="align-right bright max-temp">
{{f.maxTemperature | roundValue | unit("temperature")}}
</td>
<td class="align-right min-temp">
{{f.minTemperature | roundValue | unit("temperature")}}
</td>
{% if config.showRainAmount %}
<td class="align-right bright rain">
{{f.rain | formatRain}}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% else %}
{{"LOADING" | translate}}
{% endif %}

<!-- Unclomment the line below to see the contents of the `current` object. -->
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{forecast | dump}}</div> -->
Binary file added modules/default/weather/forecast.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 102 additions & 0 deletions modules/default/weather/providers/darksky.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* global WeatherProvider, WeatherDay */

/* Magic Mirror
* Module: Weather
* Provider: Dark Sky
*
* By Nicholas Hubbard https://github.com/nhubbard
* MIT Licensed
*
* This class is a provider for Dark Sky.
*/
WeatherProvider.register("darksky", {
// Set the name of the provider.
// Not strictly required, but helps for debugging.
providerName: "Dark Sky",

fetchCurrentWeather: function() {
this.fetchData(this.getUrl())
.then(data => {
if(!data || !data.currently || typeof data.currently.temperature === "undefined") {
// No usable data?
return;
}

var currentWeather = this.generateWeatherDayFromCurrentWeather(data);
this.setCurrentWeather(currentWeather);
}).catch(function(request) {
Log.error("Could not load data ... ", request);
});
},

fetchWeatherForecast: function() {
this.fetchData(this.getUrl())
.then(data => {
if(!data || !data.daily || !data.daily.data.length) {
// No usable data?
return;
}

var forecast = this.generateWeatherObjectsFromForecast(data.daily.data);
this.setWeatherForecast(forecast);
}).catch(function(request) {
Log.error("Could not load data ... ", request);
});
},

// Create a URL from the config and base URL.
getUrl: function() {
return `${this.config.apiBase}${this.config.weatherEndpoint}/${this.config.apiKey}/${this.config.lat},${this.config.lon}`;
},

// Implement WeatherDay generator.
generateWeatherDayFromCurrentWeather: function(currentWeatherData) {
var 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(currentWeatherData.daily.data[0].sunriseTime, "X");
currentWeather.sunset = moment(currentWeatherData.daily.data[0].sunsetTime, "X");

return currentWeather;
},

generateWeatherObjectsFromForecast: function(forecasts) {
var days = [];

for (var forecast of forecasts) {
var weather = new WeatherObject();

weather.date = moment(forecast.time, "X");
weather.minTemperature = forecast.temperatureMin;
weather.maxTemperature = forecast.temperatureMax;
weather.weatherType = this.convertWeatherType(forecast.icon);
weather.rain = forecast.precipAccumulation;

days.push(weather)
}

return days
},

// Map icons from Dark Sky to our icons.
convertWeatherType: function(weatherType) {
var 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;
}
});
Loading