Merge pull request #1637 from maloakes/master

Add Weather provider UK Met Office
This commit is contained in:
Michael Teeuw 2019-06-13 16:51:09 +02:00 committed by GitHub
commit a7b83e9fe3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2700 additions and 1207 deletions

8
CHANGELOG.md Normal file → Executable file
View File

@ -18,6 +18,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Calendar module: added `nextDaysRelative` config option
- Add `broadcastPastEvents` config option for calendars to include events from the past `maximumNumberOfDays` in event broadcasts
Added UK Met Office Datapoint feed as a provider in the default weather module.
- added new provider class
- added suncalc.js dependency to calculate sun times (not provided in UK Met Office feed)
- 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
- optionally display probability of precipitation (PoP) in current weather (UK Met Office data)
### Updated
- English translation for "Feels" to "Feels like"
- Fixed the example calender url in `config.js.sample`
@ -29,6 +36,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Allowance HTML5 autoplay-policy (policy is changed from Chrome 66 updates)
- Handle SIGTERM messages
- Fixes sliceMultiDayEvents so it respects maximumNumberOfDays
>>>>>>> upstream/develop
## [2.7.1] - 2019-04-02

12
modules/default/weather/README.md Normal file → Executable file
View File

@ -35,9 +35,11 @@ The following properties can be configured:
| Option | Description
| ---------------------------- | -----------
| `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` , `darksky` , or `weathergov` <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`
| `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`
| `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)
@ -105,6 +107,14 @@ The following properties can be configured:
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
### UK Met Office options
| Option | Description
| ---------------------------- | -----------
| `apiBase` | The UKMO base URL. <br><br> **Possible value:** `'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/'` <br> This value is **REQUIRED**
| `locationId` | The UKMO API location code. <br><br> **Possible values:** `322942` <br> This value is **REQUIRED**
| `apiKey` | The [UK Met Office](https://www.metoffice.gov.uk/datapoint/getting-started) API key, which can be obtained by creating an UKMO Datapoint account. <br><br> This value is **REQUIRED**
## API Provider Development
If you want to add another API provider checkout the [Guide](providers).

15
modules/default/weather/current.njk Normal file → Executable file
View File

@ -56,11 +56,18 @@
</div>
{% endif %}
</div>
{% if config.showFeelsLike and not config.onlyTemp %}
{% if (config.showFeelsLike or config.showPrecipitationAmount) and not config.onlyTemp %}
<div class="normal medium">
<span class="dimmed">
{{ "FEELS" | translate }} {{ current.feelsLike() | roundValue | unit("temperature") | decimalSymbol }}
</span>
{% if config.showFeelsLike %}
<span class="dimmed">
{{ "FEELS" | translate }} {{ current.feelsLike() | roundValue | unit("temperature") | decimalSymbol }}
</span>
{% endif %}
{% if config.showPrecipitationAmount %}
<span class="dimmed">
{{ "PRECIP" | translate }} {{ current.precipitation | unit("precip") }}
</span>
{% endif %}
</div>
{% endif %}
{% else %}

6
modules/default/weather/providers/README.md Normal file → Executable file
View File

@ -91,7 +91,9 @@ A convenience function to make requests. It returns a promise.
| Property | Type | Value/Unit |
| --- | --- | --- |
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric` and `imperial` |
| 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. |
| windSpeed |`number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
| windDirection |`number` | Direction of the wind in degrees. |
@ -104,7 +106,7 @@ A convenience function to make requests. It returns a promise.
| humidity | `number` | Percentage of humidity |
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` |
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
#### 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.
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.humidity = parseFloat(currentWeatherData.currently.humidity);
@ -76,7 +76,7 @@ WeatherProvider.register("darksky", {
const days = [];
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.minTemperature = forecast.temperatureMin;

10
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
*/
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.temperature = currentWeatherData.main.temp;
@ -92,7 +92,7 @@ WeatherProvider.register("openweathermap", {
return this.fetchForecastDaily(forecasts);
}
// 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;
},
@ -109,7 +109,7 @@ WeatherProvider.register("openweathermap", {
let snow = 0;
// variable for 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) {
@ -123,7 +123,7 @@ WeatherProvider.register("openweathermap", {
// push weather information to days array
days.push(weather);
// create new weather-object
weather = new WeatherObject(this.config.units);
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
minTemp = [];
maxTemp = [];
@ -187,7 +187,7 @@ WeatherProvider.register("openweathermap", {
const days = [];
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.minTemperature = forecast.temp.min;

View File

@ -0,0 +1,262 @@
/* global WeatherProvider, WeatherObject */
/* Magic Mirror
* Module: Weather
*
* By Malcolm Oakes https://github.com/maloakes
* MIT Licensed.
*
* This class is a provider for UK Met Office Datapoint.
*/
WeatherProvider.register("ukmetoffice", {
// Set the name of the provider.
// This isn't strictly necessary, since it will fallback to the provider identifier
// But for debugging (and future alerts) it would be nice to have the real name.
providerName: "UK Met Office",
units: {
imperial: "us",
metric: "si"
},
// Overwrite the fetchCurrentWeather method.
fetchCurrentWeather() {
this.fetchData(this.getUrl("3hourly"))
.then(data => {
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location ||
!data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length == 0) {
// Did not receive usable new data.
// Maybe this needs a better check?
return;
}
this.setFetchedLocation(`${data.SiteRep.DV.Location.name}, ${data.SiteRep.DV.Location.country}`);
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
this.setCurrentWeather(currentWeather);
})
.catch(function(request) {
Log.error("Could not load data ... ", request);
})
},
// Overwrite the fetchCurrentWeather method.
fetchWeatherForecast() {
this.fetchData(this.getUrl("daily"))
.then(data => {
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location ||
!data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length == 0) {
// Did not receive usable new data.
// Maybe this needs a better check?
return;
}
this.setFetchedLocation(`${data.SiteRep.DV.Location.name}, ${data.SiteRep.DV.Location.country}`);
const forecast = this.generateWeatherObjectsFromForecast(data);
this.setWeatherForecast(forecast);
})
.catch(function(request) {
Log.error("Could not load data ... ", request);
})
},
/** UK Met Office Specific Methods - These are not part of the default provider methods */
/*
* Gets the complete url for the request
*/
getUrl(forecastType) {
return this.config.apiBase + this.config.locationID + this.getParams(forecastType);
},
/*
* Generate a WeatherObject based on currentWeatherInformation
*/
generateWeatherObjectFromCurrentWeather(currentWeatherData) {
const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
// data times are always UTC
let nowUtc = moment.utc()
let midnightUtc = nowUtc.clone().startOf("day")
let timeInMins = nowUtc.diff(midnightUtc, "minutes");
// loop round each of the (5) periods, look for today (the first period may be yesterday)
for (i in currentWeatherData.SiteRep.DV.Location.Period) {
let periodDate = moment.utc(currentWeatherData.SiteRep.DV.Location.Period[i].value.substr(0,10), "YYYY-MM-DD")
// ignore if period is before today
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
// check this is the period we want, after today the diff will be -ve
if (moment().diff(periodDate, "minutes") > 0) {
// loop round the reports looking for the one we are in
// $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
for (j in currentWeatherData.SiteRep.DV.Location.Period[i].Rep){
let p = currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].$;
if (timeInMins >= p && timeInMins-180 < p) {
// finally got the one we want, so populate weather object
currentWeather.humidity = currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].H;
currentWeather.temperature = this.convertTemp(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].T);
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].F);
currentWeather.precipitation = parseInt(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].Pp);
currentWeather.windSpeed = this.convertWindSpeed(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].S);
currentWeather.windDirection = this.convertWindDirection(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].D);
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].W);
}
}
}
}
}
// determine the sunrise/sunset times - not supplied in UK Met Office data
let times = this.calcAstroData(currentWeatherData.SiteRep.DV.Location)
currentWeather.sunrise = times[0];
currentWeather.sunset = times[1];
return currentWeather;
},
/*
* Generate WeatherObjects based on forecast information
*/
generateWeatherObjectsFromForecast(forecasts) {
const days = [];
// loop round the (5) periods getting the data
// for each period array, Day is [0], Night is [1]
for (j in forecasts.SiteRep.DV.Location.Period) {
const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
// data times are always UTC
dateStr = forecasts.SiteRep.DV.Location.Period[j].value
let periodDate = moment.utc(dateStr.substr(0,10), "YYYY-MM-DD")
// ignore if period is before today
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
// populate the weather object
weather.date = moment.utc(dateStr.substr(0,10), "YYYY-MM-DD");
weather.minTemperature = this.convertTemp(forecasts.SiteRep.DV.Location.Period[j].Rep[1].Nm);
weather.maxTemperature = this.convertTemp(forecasts.SiteRep.DV.Location.Period[j].Rep[0].Dm);
weather.weatherType = this.convertWeatherType(forecasts.SiteRep.DV.Location.Period[j].Rep[0].W);
weather.precipitation = parseInt(forecasts.SiteRep.DV.Location.Period[j].Rep[0].PPd);
days.push(weather);
}
}
return days;
},
/*
* calculate the astronomical data
*/
calcAstroData(location) {
const sunTimes = [];
// determine the sunrise/sunset times
let times = SunCalc.getTimes(new Date(), location.lat, location.lon);
sunTimes.push(moment(times.sunrise, "X"));
sunTimes.push(moment(times.sunset, "X"));
return sunTimes;
},
/*
* Convert the Met Office icons to a more usable name.
*/
convertWeatherType(weatherType) {
const weatherTypes = {
0: "night-clear",
1: "day-sunny",
2: "night-alt-cloudy",
3: "day-cloudy",
5: "fog",
6: "fog",
7: "cloudy",
8: "cloud",
9: "night-sprinkle",
10: "day-sprinkle",
11: "raindrops",
12: "sprinkle",
13: "night-alt-showers",
14: "day-showers",
15: "rain",
16: "night-alt-sleet",
17: "day-sleet",
18: "sleet",
19: "night-alt-hail",
20: "day-hail",
21: "hail",
22: "night-alt-snow",
23: "day-snow",
24: "snow",
25: "night-alt-snow",
26: "day-snow",
27: "snow",
28: "night-alt-thunderstorm",
29: "day-thunderstorm",
30: "thunderstorm"
};
return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null;
},
/*
* Convert temp (from degrees C) if required
*/
convertTemp(tempInC) {
return this.tempUnits === "imperial" ? tempInC * 9 / 5 + 32 : tempInC;
},
/*
* Convert wind speed (from mph) if required
*/
convertWindSpeed(windInMph) {
return this.windUnits === "metric" ? windInMph * 2.23694 : windInMph;
},
/*
* Convert the wind direction cardinal to value
*/
convertWindDirection(windDirection) {
const windCardinals = {
"N": 0,
"NNE": 22,
"NE": 45,
"ENE": 67,
"E": 90,
"ESE": 112,
"SE": 135,
"SSE": 157,
"S": 180,
"SSW": 202,
"SW": 225,
"WSW": 247,
"W": 270,
"WNW": 292,
"NW": 315,
"NNW": 337
};
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
},
/*
* Generates an url with api parameters based on the config.
*
* return String - URL params.
*/
getParams(forecastType) {
let params = "?";
params += "res=" + forecastType;
params += "&key=" + this.config.apiKey;
return params;
}
});

6
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
*/
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.windSpeed = currentWeatherData.windSpeed.split(" ", 1);
@ -95,7 +95,7 @@ WeatherProvider.register("weathergov", {
let maxTemp = [];
// variable for 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;
for (const forecast of forecasts) {
@ -109,7 +109,7 @@ WeatherProvider.register("weathergov", {
// push weather information to days array
days.push(weather);
// create new weather-object
weather = new WeatherObject(this.config.units);
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
minTemp = [];
maxTemp = [];

20
modules/default/weather/weather.js Normal file → Executable file
View File

@ -19,6 +19,10 @@ Module.register("weather",{
locationID: false,
appid: "",
units: config.units,
tempUnits: config.units,
windUnits: config.units,
updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000,
timeFormat: config.timeFormat,
@ -68,13 +72,14 @@ Module.register("weather",{
"moment.js",
"weatherprovider.js",
"weatherobject.js",
"suncalc.js",
this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")
];
},
// Override getHeader method.
getHeader: function() {
if (this.config.appendLocationNameToHeader && this.weatherProvider) {
if (this.config.appendLocationNameToHeader && this.data.header !== undefined && this.weatherProvider) {
return this.data.header + " " + this.weatherProvider.fetchedLocation();
}
@ -84,6 +89,7 @@ Module.register("weather",{
// Start the weather module.
start: function () {
moment.locale(this.config.lang);
// Initialize the weather provider.
this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this);
@ -188,13 +194,13 @@ Module.register("weather",{
this.nunjucksEnvironment().addFilter("unit", function (value, type) {
if (type === "temperature") {
if (this.config.units === "metric" || this.config.units === "imperial") {
if (this.config.tempUnits === "metric" || this.config.tempUnits === "imperial") {
value += "°";
}
if (this.config.degreeLabel) {
if (this.config.units === "metric") {
if (this.config.tempUnits === "metric") {
value += "C";
} else if (this.config.units === "imperial") {
} else if (this.config.tempUnits === "imperial") {
value += "F";
} else {
value += "K";
@ -204,7 +210,11 @@ Module.register("weather",{
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
value = "";
} else {
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
if (this.config.weatherProvider === "ukmetoffice") {
value += "%"
} else {
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
}
}
} else if (type === "humidity") {
value += "%"

18
modules/default/weather/weatherobject.js Normal file → Executable file
View File

@ -13,8 +13,11 @@
// As soon as we start implementing the forecast, mode properties will be added.
class WeatherObject {
constructor(units) {
constructor(units, tempUnits, windUnits) {
this.units = units;
this.tempUnits = tempUnits;
this.windUnits = windUnits;
this.date = null;
this.windSpeed = null;
this.windDirection = null;
@ -28,6 +31,8 @@ class WeatherObject {
this.rain = null;
this.snow = null;
this.precipitation = null;
this.feelsLikeTemp = null;
}
cardinalWindDirection() {
@ -67,7 +72,7 @@ class WeatherObject {
}
beaufortWindSpeed() {
const windInKmh = this.units === "imperial" ? 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];
for (const [index, speed] of speeds.entries()) {
if (speed > windInKmh) {
@ -82,8 +87,11 @@ class WeatherObject {
}
feelsLike() {
const windInMph = this.units === "imperial" ? this.windSpeed : this.windSpeed * 2.23694;
const tempInF = this.units === "imperial" ? this.temperature : this.temperature * 9 / 5 + 32;
if (this.feelsLikeTemp) {
return this.feelsLikeTemp
}
const windInMph = (this.windUnits === "imperial") ? this.windSpeed : this.windSpeed * 2.23694;
const tempInF = this.tempUnits === "imperial" ? this.temperature : this.temperature * 9 / 5 + 32;
let feelsLike = tempInF;
if (windInMph > 3 && tempInF < 50) {
@ -97,6 +105,6 @@ class WeatherObject {
- 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;
}
}

3491
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -46,11 +46,11 @@
"grunt-yamllint": "latest",
"http-auth": "^3.2.3",
"jsdom": "^11.6.2",
"jshint": "^2.9.5",
"jshint": "^2.10.2",
"mocha": "^4.1.0",
"mocha-each": "^1.1.0",
"spectron": "^3.8.0",
"stylelint": "^8.4.0",
"stylelint": "^9.10.1",
"stylelint-config-standard": "latest",
"time-grunt": "latest"
},

3
translations/en.json Normal file → Executable file
View File

@ -31,5 +31,6 @@
"UPDATE_INFO_SINGLE": "The current installation is {COMMIT_COUNT} commit behind on the {BRANCH_NAME} branch.",
"UPDATE_INFO_MULTIPLE": "The current installation is {COMMIT_COUNT} commits behind on the {BRANCH_NAME} branch.",
"FEELS": "Feels like"
"FEELS": "Feels like",
"PRECIP": "PoP"
}

30
vendor/package-lock.json generated vendored
View File

@ -703,7 +703,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
"optional": true,
"requires": {
"is-glob": "^2.0.0"
}
@ -717,8 +716,7 @@
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"invert-kv": {
"version": "1.0.0",
@ -737,8 +735,7 @@
"is-buffer": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
"optional": true
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw="
},
"is-dotfile": {
"version": "1.0.3",
@ -764,8 +761,7 @@
"is-extglob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
"optional": true
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
@ -779,7 +775,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"optional": true,
"requires": {
"is-extglob": "^1.0.0"
}
@ -808,8 +803,7 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"isobject": {
"version": "2.1.0",
@ -824,7 +818,6 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
@ -890,7 +883,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
@ -1039,14 +1031,12 @@
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
"optional": true
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
},
"repeat-element": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
"optional": true
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo="
},
"repeat-string": {
"version": "1.6.1",
@ -1057,8 +1047,7 @@
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"optional": true
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"set-immediate-shim": {
"version": "1.0.1",
@ -1093,6 +1082,11 @@
"ansi-regex": "^2.0.0"
}
},
"suncalc": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz",
"integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

1
vendor/package.json vendored Normal file → Executable file
View File

@ -14,6 +14,7 @@
"moment": "^2.17.1",
"moment-timezone": "^0.5.11",
"nunjucks": "^3.0.1",
"suncalc": "^1.8.0",
"weathericons": "^2.1.0"
}
}

3
vendor/vendor.js vendored Normal file → Executable file
View File

@ -13,7 +13,8 @@ var vendor = {
"weather-icons.css": "node_modules/weathericons/css/weather-icons.css",
"weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css",
"font-awesome.css": "css/font-awesome.css",
"nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js"
"nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js",
"suncalc.js": "node_modules/suncalc/suncalc.js"
};
if (typeof module !== "undefined"){module.exports = vendor;}