2020-05-05 14:55:15 +02:00
/* global WeatherProvider */
2017-09-21 16:38:18 +02:00
/ * M a g i c M i r r o r
* Module : Weather
*
2020-04-28 23:05:28 +02:00
* By Michael Teeuw https : //michaelteeuw.nl
2017-09-21 16:38:18 +02:00
* MIT Licensed .
* /
2020-05-11 22:22:32 +02:00
Module . register ( "weather" , {
2017-09-21 16:38:18 +02:00
// Default module config.
defaults : {
2017-09-22 12:26:47 +02:00
weatherProvider : "openweathermap" ,
2017-09-22 13:26:44 +02:00
roundTemp : false ,
2020-08-01 17:39:58 -04:00
type : "current" , // current, forecast, daily (equivalent to forecast), hourly (only with OpenWeatherMap /onecall endpoint)
2020-06-30 12:41:14 -04:00
lat : 0 ,
lon : 0 ,
2017-10-18 11:58:45 +02:00
location : false ,
locationID : false ,
units : config . units ,
2020-11-07 09:54:13 +01:00
useKmh : false ,
2019-06-07 15:27:08 +01:00
tempUnits : config . units ,
windUnits : config . units ,
2017-10-18 11:58:45 +02:00
updateInterval : 10 * 60 * 1000 , // every 10 minutes
animationSpeed : 1000 ,
timeFormat : config . timeFormat ,
showPeriod : true ,
showPeriodUpper : false ,
showWindDirection : true ,
showWindDirectionAsArrow : false ,
useBeaufort : true ,
lang : config . language ,
showHumidity : false ,
2020-05-28 10:10:00 +02:00
showSun : true ,
2017-10-18 11:58:45 +02:00
degreeLabel : false ,
2019-01-04 19:46:54 +01:00
decimalSymbol : "." ,
2017-10-18 11:58:45 +02:00
showIndoorTemperature : false ,
showIndoorHumidity : false ,
2019-01-06 12:34:44 +01:00
maxNumberOfDays : 5 ,
2020-06-30 02:40:41 -04:00
maxEntries : 5 ,
2019-01-06 12:24:26 +01:00
fade : true ,
fadePoint : 0.25 , // Start on 1/4th of the list.
2017-10-18 11:58:45 +02:00
initialLoadDelay : 0 , // 0 seconds delay
retryDelay : 2500 ,
2020-07-04 21:40:31 +02:00
apiKey : "" ,
apiSecret : "" ,
2017-10-18 11:58:45 +02:00
apiVersion : "2.5" ,
2020-07-04 21:49:14 +02:00
apiBase : "https://api.openweathermap.org/data/" , // TODO: this should not be part of the weather.js file, but should be contained in the openweatherprovider
2018-12-27 17:13:49 +01:00
weatherEndpoint : "/weather" ,
2017-10-18 11:58:45 +02:00
appendLocationNameToHeader : true ,
calendarClass : "calendar" ,
2018-12-27 18:52:35 +01:00
tableClass : "small" ,
2017-10-18 11:58:45 +02:00
onlyTemp : false ,
2019-02-14 13:00:40 -06:00
showPrecipitationAmount : false ,
2018-12-30 14:17:13 +01:00
colored : false ,
2020-12-31 18:58:21 +01:00
showFeelsLike : true ,
feelsLikeWithDegree : false
2017-09-21 16:38:18 +02:00
} ,
// Module properties.
weatherProvider : null ,
2017-09-22 12:26:47 +02:00
// Define required scripts.
2020-05-11 22:22:32 +02:00
getStyles : function ( ) {
2017-10-18 13:38:56 +02:00
return [ "font-awesome.css" , "weather-icons.css" , "weather.css" ] ;
2017-09-22 12:26:47 +02:00
} ,
2019-01-17 08:54:16 -06:00
// Return the scripts that are necessary for the weather module.
2017-09-21 16:38:18 +02:00
getScripts : function ( ) {
2020-05-11 22:22:32 +02:00
return [ "moment.js" , "weatherprovider.js" , "weatherobject.js" , "suncalc.js" , this . file ( "providers/" + this . config . weatherProvider . toLowerCase ( ) + ".js" ) ] ;
2017-09-21 16:38:18 +02:00
} ,
2018-12-27 17:13:49 +01:00
// Override getHeader method.
2020-05-11 22:22:32 +02:00
getHeader : function ( ) {
2020-08-01 12:30:59 +02:00
if ( this . config . appendLocationNameToHeader && this . weatherProvider ) {
if ( this . data . header ) return this . data . header + " " + this . weatherProvider . fetchedLocation ( ) ;
else return this . weatherProvider . fetchedLocation ( ) ;
2018-12-27 17:13:49 +01:00
}
2020-08-01 12:30:59 +02:00
return this . data . header ? this . data . header : "" ;
2018-12-27 17:13:49 +01:00
} ,
2017-09-21 16:38:18 +02:00
// Start the weather module.
start : function ( ) {
2018-12-27 17:13:49 +01:00
moment . locale ( this . config . lang ) ;
2019-06-07 15:27:08 +01:00
2017-09-21 16:38:18 +02:00
// Initialize the weather provider.
2018-05-21 10:56:46 +02:00
this . weatherProvider = WeatherProvider . initialize ( this . config . weatherProvider , this ) ;
2017-09-21 16:38:18 +02:00
// Let the weather provider know we are starting.
2018-05-21 10:56:46 +02:00
this . weatherProvider . start ( ) ;
2017-09-21 16:38:18 +02:00
2020-12-31 18:58:21 +01:00
this . config . feelsLikeWithDegree = this . translate ( "FEELS" ) . indexOf ( "{DEGREE}" ) > - 1 ;
2017-10-18 13:38:56 +02:00
// Add custom filters
2018-05-21 10:56:46 +02:00
this . addFilters ( ) ;
2017-10-18 13:38:56 +02:00
2017-09-22 12:26:47 +02:00
// Schedule the first update.
2018-12-27 19:37:02 +01:00
this . scheduleUpdate ( this . config . initialLoadDelay ) ;
2017-09-21 16:38:18 +02:00
} ,
2018-07-02 15:43:24 +02:00
// Override notification handler.
2020-05-11 22:22:32 +02:00
notificationReceived : function ( notification , payload , sender ) {
2018-07-02 15:43:24 +02:00
if ( notification === "CALENDAR_EVENTS" ) {
var senderClasses = sender . data . classes . toLowerCase ( ) . split ( " " ) ;
if ( senderClasses . indexOf ( this . config . calendarClass . toLowerCase ( ) ) !== - 1 ) {
this . firstEvent = false ;
for ( var e in payload ) {
var event = payload [ e ] ;
if ( event . location || event . geo ) {
this . firstEvent = event ;
//Log.log("First upcoming event with location: ", event);
break ;
}
}
}
2018-12-27 19:37:02 +01:00
} else if ( notification === "INDOOR_TEMPERATURE" ) {
2018-07-02 15:43:24 +02:00
this . indoorTemperature = this . roundValue ( payload ) ;
this . updateDom ( 300 ) ;
2018-12-27 19:37:02 +01:00
} else if ( notification === "INDOOR_HUMIDITY" ) {
2018-07-02 15:43:24 +02:00
this . indoorHumidity = this . roundValue ( payload ) ;
this . updateDom ( 300 ) ;
}
} ,
2017-10-01 13:50:15 +02:00
// Select the template depending on the display type.
getTemplate : function ( ) {
2020-08-01 17:39:58 -04:00
switch ( this . config . type . toLowerCase ( ) ) {
case "current" :
return ` current.njk ` ;
case "hourly" :
return ` hourly.njk ` ;
case "daily" :
case "forecast" :
return ` forecast.njk ` ;
default :
return ` ${ this . config . type . toLowerCase ( ) } .njk ` ;
2020-08-01 02:59:08 -04:00
}
2017-10-01 13:50:15 +02:00
} ,
// Add all the data to the template.
getTemplateData : function ( ) {
return {
config : this . config ,
current : this . weatherProvider . currentWeather ( ) ,
2018-07-02 15:43:24 +02:00
forecast : this . weatherProvider . weatherForecast ( ) ,
2020-08-01 02:59:08 -04:00
weatherData : this . weatherProvider . weatherData ( ) ,
2018-07-02 15:43:24 +02:00
indoor : {
humidity : this . indoorHumidity ,
temperature : this . indoorTemperature
}
2019-06-05 10:23:58 +02:00
} ;
2017-09-21 16:38:18 +02:00
} ,
// What to do when the weather provider has new information available?
2020-05-11 22:22:32 +02:00
updateAvailable : function ( ) {
2018-05-21 10:56:46 +02:00
Log . log ( "New weather information available." ) ;
2018-12-27 19:37:02 +01:00
this . updateDom ( 0 ) ;
this . scheduleUpdate ( ) ;
2017-09-22 12:26:47 +02:00
} ,
2020-05-11 22:22:32 +02:00
scheduleUpdate : function ( delay = null ) {
2017-09-22 12:26:47 +02:00
var nextLoad = this . config . updateInterval ;
if ( delay !== null && delay >= 0 ) {
nextLoad = delay ;
}
setTimeout ( ( ) => {
2020-08-01 02:59:08 -04:00
if ( this . config . weatherEndpoint === "/onecall" ) {
this . weatherProvider . fetchWeatherData ( ) ;
} else if ( this . config . type === "forecast" ) {
2018-05-21 10:56:46 +02:00
this . weatherProvider . fetchWeatherForecast ( ) ;
2020-06-30 04:18:03 -04:00
} else {
2018-05-21 10:56:46 +02:00
this . weatherProvider . fetchCurrentWeather ( ) ;
2017-09-22 13:26:44 +02:00
}
2017-09-22 12:26:47 +02:00
} , nextLoad ) ;
2017-10-18 13:38:56 +02:00
} ,
2020-05-11 22:22:32 +02:00
roundValue : function ( temperature ) {
2018-07-02 15:43:24 +02:00
var decimals = this . config . roundTemp ? 0 : 1 ;
return parseFloat ( temperature ) . toFixed ( decimals ) ;
2018-12-27 17:13:49 +01:00
} ,
2018-07-02 15:43:24 +02:00
2017-10-18 13:38:56 +02:00
addFilters ( ) {
2020-05-11 22:22:32 +02:00
this . nunjucksEnvironment ( ) . addFilter (
"formatTime" ,
function ( date ) {
date = moment ( date ) ;
if ( this . config . timeFormat !== 24 ) {
if ( this . config . showPeriod ) {
if ( this . config . showPeriodUpper ) {
return date . format ( "h:mm A" ) ;
} else {
return date . format ( "h:mm a" ) ;
}
2017-10-18 13:38:56 +02:00
} else {
2020-05-11 22:22:32 +02:00
return date . format ( "h:mm" ) ;
2017-10-18 13:38:56 +02:00
}
2018-05-21 10:56:46 +02:00
}
2020-05-11 22:22:32 +02:00
return date . format ( "HH:mm" ) ;
} . bind ( this )
) ;
2018-05-21 10:56:46 +02:00
2020-05-11 22:22:32 +02:00
this . nunjucksEnvironment ( ) . addFilter (
"unit" ,
function ( value , type ) {
if ( type === "temperature" ) {
if ( this . config . tempUnits === "metric" || this . config . tempUnits === "imperial" ) {
value += "°" ;
2018-05-21 10:56:46 +02:00
}
2020-05-11 22:22:32 +02:00
if ( this . config . degreeLabel ) {
if ( this . config . tempUnits === "metric" ) {
value += "C" ;
} else if ( this . config . tempUnits === "imperial" ) {
value += "F" ;
} else {
value += "K" ;
}
}
} else if ( type === "precip" ) {
2020-07-01 05:08:04 -04:00
if ( value === null || isNaN ( value ) || value === 0 || value . toFixed ( 2 ) === "0.00" ) {
2020-05-11 22:22:32 +02:00
value = "" ;
2018-05-21 10:56:46 +02:00
} else {
2020-06-28 11:00:10 +01:00
if ( this . config . weatherProvider === "ukmetoffice" || this . config . weatherProvider === "ukmetofficedatahub" ) {
2020-05-11 22:22:32 +02:00
value += "%" ;
} else {
value = ` ${ value . toFixed ( 2 ) } ${ this . config . units === "imperial" ? "in" : "mm" } ` ;
}
2018-05-21 10:56:46 +02:00
}
2020-05-11 22:22:32 +02:00
} else if ( type === "humidity" ) {
value += "%" ;
2017-10-18 13:38:56 +02:00
}
2020-05-11 22:22:32 +02:00
return value ;
} . bind ( this )
) ;
this . nunjucksEnvironment ( ) . addFilter (
"roundValue" ,
function ( value ) {
return this . roundValue ( value ) ;
} . bind ( this )
) ;
this . nunjucksEnvironment ( ) . addFilter (
"decimalSymbol" ,
function ( value ) {
return value . toString ( ) . replace ( /\./g , this . config . decimalSymbol ) ;
} . bind ( this )
) ;
this . nunjucksEnvironment ( ) . addFilter (
"calcNumSteps" ,
function ( forecast ) {
return Math . min ( forecast . length , this . config . maxNumberOfDays ) ;
} . bind ( this )
) ;
2020-06-30 02:40:41 -04:00
this . nunjucksEnvironment ( ) . addFilter (
"calcNumEntries" ,
function ( dataArray ) {
2020-06-30 04:18:03 -04:00
return Math . min ( dataArray . length , this . config . maxEntries ) ;
2020-06-30 02:40:41 -04:00
} . bind ( this )
) ;
2020-05-11 22:22:32 +02:00
this . nunjucksEnvironment ( ) . addFilter (
"opacity" ,
function ( currentStep , numSteps ) {
if ( this . config . fade && this . config . fadePoint < 1 ) {
if ( this . config . fadePoint < 0 ) {
this . config . fadePoint = 0 ;
}
var startingPoint = numSteps * this . config . fadePoint ;
var numFadesteps = numSteps - startingPoint ;
if ( currentStep >= startingPoint ) {
return 1 - ( currentStep - startingPoint ) / numFadesteps ;
} else {
return 1 ;
}
2019-01-06 12:24:26 +01:00
} else {
return 1 ;
}
2020-05-11 22:22:32 +02:00
} . bind ( this )
) ;
2017-10-01 16:19:14 +02:00
}
2017-09-21 16:38:18 +02:00
} ) ;