diff --git a/CHANGELOG.md b/CHANGELOG.md
index d5a6239a..7963d746 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ Special thanks to the following contributors: @B1gG, @codac, @ezeholz, @khassel,
- Added custom-properties for colors and fonts for improved styling experience, see `custom.css.sample` file
- Added custom-properties for gaps around body and between modules
- Added test case for recurring calendar events
+- Added new Environment Canada provider for default WEATHER module (weather data for Canadian locations only)
### Updated
diff --git a/modules/default/weather/forecast.njk b/modules/default/weather/forecast.njk
index eeead6b2..92a575cf 100644
--- a/modules/default/weather/forecast.njk
+++ b/modules/default/weather/forecast.njk
@@ -20,9 +20,15 @@
{{ f.minTemperature | roundValue | unit("temperature") | decimalSymbol }}
{% if config.showPrecipitationAmount %}
-
- {{ f.precipitation | unit("precip") }}
- |
+ {% if f.precipitationUnits %}
+
+ {{ f.precipitation }}{{ f.precipitationUnits }}
+ |
+ {% else %}
+
+ {{ f.precipitation | unit("precip") }}
+ |
+ {% endif %}
{% endif %}
{% set currentStep = currentStep + 1 %}
diff --git a/modules/default/weather/hourly.njk b/modules/default/weather/hourly.njk
index 3950ece2..38832bdb 100644
--- a/modules/default/weather/hourly.njk
+++ b/modules/default/weather/hourly.njk
@@ -11,6 +11,10 @@
{{ hour.temperature | roundValue | unit("temperature") }}
{% if config.showPrecipitationAmount %}
+
+ {{ hour.precipitation }}{{ hour.precipitationUnits }}
+ |
+ {% else %}
{{ hour.precipitation | unit("precip") }}
|
diff --git a/modules/default/weather/providers/envcanada.js b/modules/default/weather/providers/envcanada.js
new file mode 100644
index 00000000..c48d26dc
--- /dev/null
+++ b/modules/default/weather/providers/envcanada.js
@@ -0,0 +1,664 @@
+/* global WeatherProvider, WeatherObject */
+
+/* Magic Mirror
+ * Module: Weather
+ * Provider: Environment Canada (EC)
+ *
+ * This class is a provider for Environment Canada MSC Datamart
+ * Note that this is only for Canadian locations and does not require an API key (access is anonymous)
+ *
+ * EC Documentation at following links:
+ * https://dd.weather.gc.ca/citypage_weather/schema/
+ * https://eccc-msc.github.io/open-data/msc-datamart/readme_en/
+ *
+ * This module supports Canadian locations only and requires 2 additional config parms:
+ *
+ * siteCode - the city/town unique identifier for which weather is to be displayed. Format is 's0000000'.
+ *
+ * provCode - the 2-character province code for the selected city/town.
+ *
+ * Example: for Toronto, Ontario, the following parms would be used
+ *
+ * siteCode: 's0000458',
+ * provCode: 'ON'
+ *
+ * To determine the siteCode and provCode values for a Canadian city/town, look at the Environment Canada document
+ * at https://dd.weather.gc.ca/citypage_weather/docs/site_list_en.csv (or site_list_fr.csv). There you will find a table
+ * with locations you can search under column B (English Names), with the corresponding siteCode under
+ * column A (Codes) and provCode under column C (Province).
+ *
+ * Original by Kevin Godin
+ *
+ * License to use Environment Canada (EC) data is detailed here:
+ * https://eccc-msc.github.io/open-data/licence/readme_en/
+ *
+ */
+
+WeatherProvider.register("envcanada", {
+ // Set the name of the provider for debugging and alerting purposes (eg. provide eye-catcher)
+ providerName: "Environment Canada",
+
+ // Set the default config properties that is specific to this provider
+ defaults: {
+ siteCode: "s1234567",
+ provCode: "ON"
+ },
+
+ //
+ // Set config values (equates to weather module config values). Also set values pertaining to caching of
+ // Today's temperature forecast (for use in the Forecast functions below)
+ //
+ setConfig: function (config) {
+ this.config = config;
+
+ this.todayTempCacheMin = 0;
+ this.todayTempCacheMax = 0;
+ this.todayCached = false;
+ this.cacheCurrentTemp = 999;
+ },
+
+ //
+ // Called when the weather provider is started
+ //
+ start: function () {
+ Log.info(`Weather provider: ${this.providerName} started.`);
+ this.setFetchedLocation(this.config.location);
+
+ // Ensure kmH are ignored since these are custom-handled by this Provider
+
+ this.config.useKmh = false;
+ },
+
+ //
+ // Override the fetchCurrentWeather method to query EC and construct a Current weather object
+ //
+ fetchCurrentWeather() {
+ this.fetchData(this.getUrl(), "GET")
+ .then((data) => {
+ if (!data) {
+ // Did not receive usable new data.
+ return;
+ }
+ const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
+
+ this.setCurrentWeather(currentWeather);
+ })
+ .catch(function (request) {
+ Log.error("Could not load EnvCanada site data ... ", request);
+ })
+ .finally(() => this.updateAvailable());
+ },
+
+ //
+ // Override the fetchWeatherForecast method to query EC and construct Forecast weather objects
+ //
+ fetchWeatherForecast() {
+ this.fetchData(this.getUrl(), "GET")
+ .then((data) => {
+ if (!data) {
+ // Did not receive usable new data.
+ return;
+ }
+ const forecastWeather = this.generateWeatherObjectsFromForecast(data);
+
+ this.setWeatherForecast(forecastWeather);
+ })
+ .catch(function (request) {
+ Log.error("Could not load EnvCanada forecast data ... ", request);
+ })
+ .finally(() => this.updateAvailable());
+ },
+
+ //
+ // Override the fetchWeatherHourly method to query EC and construct Forecast weather objects
+ //
+ fetchWeatherHourly() {
+ this.fetchData(this.getUrl(), "GET")
+ .then((data) => {
+ if (!data) {
+ // Did not receive usable new data.
+ return;
+ }
+ const hourlyWeather = this.generateWeatherObjectsFromHourly(data);
+
+ this.setWeatherHourly(hourlyWeather);
+ })
+ .catch(function (request) {
+ Log.error("Could not load EnvCanada hourly data ... ", request);
+ })
+ .finally(() => this.updateAvailable());
+ },
+
+ //
+ // Override fetchData function to handle XML document (base function assumes JSON)
+ //
+ fetchData: function (url, method = "GET", data = null) {
+ return new Promise(function (resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.open(method, url, true);
+ request.onreadystatechange = function () {
+ if (this.readyState === 4) {
+ if (this.status === 200) {
+ resolve(this.responseXML);
+ } else {
+ reject(request);
+ }
+ }
+ };
+ request.send();
+ });
+ },
+
+ //////////////////////////////////////////////////////////////////////////////////
+ //
+ // Environment Canada methods - not part of the standard Provider methods
+ //
+ //////////////////////////////////////////////////////////////////////////////////
+
+ //
+ // Build the EC URL based on the Site Code and Province Code specified in the config parms. Note that the
+ // URL defaults to the Englsih version simply because there is no language dependancy in the data
+ // being accessed. This is only pertinent when using the EC data elements that contain a textual forecast.
+ //
+ // Also note that access is supported through a proxy service (thingproxy.freeboard.io) to mitigate
+ // CORS errors when accessing EC
+ //
+ getUrl() {
+ var path = "https://thingproxy.freeboard.io/fetch/https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml";
+
+ return path;
+ },
+
+ //
+ // Generate a WeatherObject based on current EC weather conditions
+ //
+
+ generateWeatherObjectFromCurrentWeather(ECdoc) {
+ const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
+
+ // There are instances where EC will update weather data and current temperature will not be
+ // provided. While this is a defect in the EC systems, we need to accommodate to avoid a current temp
+ // of NaN being displayed. Therefore... whenever we get a valid current temp from EC, we will cache
+ // the value. Whenever EC data is missing current temp, we will provide the cached value
+ // instead. This is reasonable since the cached value will typically be accurate within the previous
+ // hour. The only time this does not work as expected is when MM is restarted and the first query to
+ // EC finds no current temp. In this scenario, MM will end up displaying a current temp of null;
+
+ if (ECdoc.querySelector("siteData currentConditions temperature").textContent) {
+ currentWeather.temperature = this.convertTemp(ECdoc.querySelector("siteData currentConditions temperature").textContent);
+ this.cacheCurrentTemp = currentWeather.temperature;
+ } else {
+ currentWeather.temperature = this.cacheCurrentTemp;
+ }
+
+ currentWeather.windSpeed = this.convertWind(ECdoc.querySelector("siteData currentConditions wind speed").textContent);
+
+ currentWeather.windDirection = ECdoc.querySelector("siteData currentConditions wind bearing").textContent;
+
+ currentWeather.humidity = ECdoc.querySelector("siteData currentConditions relativeHumidity").textContent;
+
+ // Ensure showPrecipitationAmount is forced to false. EC does not really provide POP for current day
+ // and this feature for the weather module (current only) is sort of broken in that it wants
+ // to say POP but will display precip as an accumulated amount vs. a percentage.
+
+ this.config.showPrecipitationAmount = false;
+
+ //
+ // If the module config wants to showFeelsLike... default to the current temperature.
+ // Check for EC wind chill and humidex values and overwrite the feelsLikeTemp value.
+ // This assumes that the EC current conditions will never contain both a wind chill
+ // and humidex temperature.
+ //
+
+ if (this.config.showFeelsLike) {
+ currentWeather.feelsLikeTemp = currentWeather.temperature;
+
+ if (ECdoc.querySelector("siteData currentConditions windChill")) {
+ currentWeather.feelsLikeTemp = this.convertTemp(ECdoc.querySelector("siteData currentConditions windChill").textContent);
+ }
+
+ if (ECdoc.querySelector("siteData currentConditions humidex")) {
+ currentWeather.feelsLikeTemp = this.convertTemp(ECdoc.querySelector("siteData currentConditions humidex").textContent);
+ }
+ }
+
+ //
+ // Need to map EC weather icon to MM weatherType values
+ //
+
+ currentWeather.weatherType = this.convertWeatherType(ECdoc.querySelector("siteData currentConditions iconCode").textContent);
+
+ //
+ // Capture the sunrise and sunset values from EC data
+ //
+
+ var sunList = ECdoc.querySelectorAll("siteData riseSet dateTime");
+
+ currentWeather.sunrise = moment(sunList[1].querySelector("timeStamp").textContent, "YYYYMMDDhhmmss");
+ currentWeather.sunset = moment(sunList[3].querySelector("timeStamp").textContent, "YYYYMMDDhhmmss");
+
+ return currentWeather;
+ },
+
+ //
+ // Generate an array of WeatherObjects based on EC weather forecast
+ //
+
+ generateWeatherObjectsFromForecast(ECdoc) {
+ // Declare an array to hold each day's forecast object
+
+ const days = [];
+
+ var weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
+
+ var foreBaseDates = ECdoc.querySelectorAll("siteData forecastGroup dateTime");
+ var baseDate = foreBaseDates[1].querySelector("timeStamp").textContent;
+
+ weather.date = moment(baseDate, "YYYYMMDDhhmmss");
+
+ var foreGroup = ECdoc.querySelectorAll("siteData forecastGroup forecast");
+
+ // For simplicity, we will only accumulate precipitation and will not try to break out
+ // rain vs snow accumulations
+
+ weather.rain = null;
+ weather.snow = null;
+ weather.precipitation = null;
+
+ //
+ // The EC forecast is held in a 12-element array - Elements 0 to 11 - with each day encompassing
+ // 2 elements. the first element for a day details the Today (daytime) forecast while the second
+ // element details the Tonight (nightime) forecast. Element 0 is always for the current day.
+ //
+ // However... the forecast is somewhat 'rolling'.
+ //
+ // If the EC forecast is queried in the morning, then Element 0 will contain Current
+ // Today and Element 1 will contain Current Tonight. From there, the next 5 days of forecast will be
+ // contained in Elements 2/3, 4/5, 6/7, 8/9, and 10/11. This module will create a 6-day forecast using
+ // all of these Elements.
+ //
+ // But, if the EC forecast is queried in late afternoon, the Current Today forecast will be rolled
+ // off and Element 0 will contain Current Tonight. From there, the next 5 days will be contained in
+ // Elements 1/2, 3/4, 5/6, 7/8, and 9/10. As well, Elelement 11 will contain a forecast for a 6th day,
+ // but only for the Today portion (not Tonight). This module will create a 6-day forecast using
+ // Elements 0 to 11, and will ignore the additional Todat forecast in Element 11.
+ //
+ // We need to determine if Element 0 is showing the forecast for Current Today or Current Tonight.
+ // This is required to understand how Min and Max temperature will be determined, and to understand
+ // where the next day's (aka Tomorrow's) forecast is located in the forecast array.
+ //
+
+ var nextDay = 0;
+ var lastDay = 0;
+ var currentTemp = ECdoc.querySelector("siteData currentConditions temperature").textContent;
+
+ //
+ // If the first Element is Current Today, look at Current Today and Current Tonight for the current day.
+ //
+
+ if (foreGroup[0].querySelector("period[textForecastName='Today']")) {
+ this.todaytempCacheMin = 0;
+ this.todaytempCacheMax = 0;
+ this.todayCached = true;
+
+ this.setMinMaxTemps(weather, foreGroup, 0, true, currentTemp);
+
+ this.setPrecipitation(weather, foreGroup, 0);
+
+ //
+ // Set the Element number that will reflect where the next day's forecast is located. Also set
+ // the Element number where the end of the forecast will be. This is important because of the
+ // rolling nature of the EC forecast. In the current scenario (Today and Tonight are present
+ // in elements 0 and 11, we know that we will have 6 full days of forecasts and we will use
+ // them. We will set lastDay such that we iterate through all 12 elements of the forecast.
+ //
+
+ nextDay = 2;
+ lastDay = 12;
+ }
+
+ //
+ // If the first Element is Current Tonight, look at Tonight only for the current day.
+ //
+ if (foreGroup[0].querySelector("period[textForecastName='Tonight']")) {
+ this.setMinMaxTemps(weather, foreGroup, 0, false, currentTemp);
+
+ this.setPrecipitation(weather, foreGroup, 0);
+
+ //
+ // Set the Element number that will reflect where the next day's forecast is located. Also set
+ // the Element number where the end of the forecast will be. This is important because of the
+ // rolling nature of the EC forecast. In the current scenario (only Current Tonight is present
+ // in Element 0, we know that we will have 6 full days of forecasts PLUS a half-day and
+ // forecast in the final element. Because we will only use full day forecasts, we set the
+ // lastDay number to ensure we ignore that final half-day (in forecast Element 11).
+ //
+
+ nextDay = 1;
+ lastDay = 11;
+ }
+
+ //
+ // Need to map EC weather icon to MM weatherType values. Always pick the first Element's icon to
+ // reflect either Today or Tonight depending on what the forecast is showing in Element 0.
+ //
+
+ weather.weatherType = this.convertWeatherType(foreGroup[0].querySelector("abbreviatedForecast iconCode").textContent);
+
+ // Push the weather object into the forecast array.
+
+ days.push(weather);
+
+ //
+ // Now do the the rest of the forecast starting at nextDay. We will process each day using 2 EC
+ // forecast Elements. This will address the fact that the EC forecast always includes Today and
+ // Tonight for each day. This is why we iterate through the forecast by a a count of 2, with each
+ // iteration looking at the current Element and the next Element.
+ //
+
+ var lastDate = moment(baseDate, "YYYYMMDDhhmmss");
+
+ for (var stepDay = nextDay; stepDay < lastDay; stepDay += 2) {
+ var weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
+
+ // Add 1 to the date to reflect the current forecast day we are building
+
+ lastDate = lastDate.add(1, "day");
+ weather.date = moment(lastDate, "X");
+
+ // Capture the temperatures for the current Element and the next Element in order to set
+ // the Min and Max temperatures for the forecast
+
+ this.setMinMaxTemps(weather, foreGroup, stepDay, true, currentTemp);
+
+ weather.rain = null;
+ weather.snow = null;
+ weather.precipitation = null;
+
+ this.setPrecipitation(weather, foreGroup, stepDay);
+
+ //
+ // Need to map EC weather icon to MM weatherType values. Always pick the first Element icon.
+ //
+
+ weather.weatherType = this.convertWeatherType(foreGroup[stepDay].querySelector("abbreviatedForecast iconCode").textContent);
+
+ // Push the weather object into the forecast array.
+
+ days.push(weather);
+ }
+
+ return days;
+ },
+
+ //
+ // Generate an array of WeatherObjects based on EC hourly weather forecast
+ //
+
+ generateWeatherObjectsFromHourly(ECdoc) {
+ // Declare an array to hold each hour's forecast object
+
+ const hours = [];
+
+ // Get local timezone UTC offset so that each hourly time can be calculated properly
+
+ var baseHours = ECdoc.querySelectorAll("siteData hourlyForecastGroup dateTime");
+ var hourOffset = baseHours[1].getAttribute("UTCOffset");
+
+ //
+ // The EC hourly forecast is held in a 24-element array - Elements 0 to 23 - with Element 0 holding
+ // the forecast for the next 'on the hour' timeslot. This means the array is a rolling 24 hours.
+ //
+
+ var hourGroup = ECdoc.querySelectorAll("siteData hourlyForecastGroup hourlyForecast");
+
+ for (var stepHour = 0; stepHour < 24; stepHour += 1) {
+ var weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
+
+ // Determine local time by applying UTC offset to the forecast timestamp
+
+ var foreTime = moment(hourGroup[stepHour].getAttribute("dateTimeUTC"), "YYYYMMDDhhmmss");
+ var currTime = foreTime.add(hourOffset, "hours");
+ weather.date = moment(currTime, "X");
+
+ // Capture the temperature
+
+ weather.temperature = this.convertTemp(hourGroup[stepHour].querySelector("temperature").textContent);
+
+ // Capture Likelihood of Precipitation (LOP) and unit-of-measure values
+
+ var precipLOP = hourGroup[stepHour].querySelector("lop").textContent * 1.0;
+
+ if (precipLOP > 0) {
+ weather.precipitation = precipLOP;
+ weather.precipitationUnits = hourGroup[stepHour].querySelector("lop").getAttribute("units");
+ }
+
+ //
+ // Need to map EC weather icon to MM weatherType values. Always pick the first Element icon.
+ //
+
+ weather.weatherType = this.convertWeatherType(hourGroup[stepHour].querySelector("iconCode").textContent);
+
+ // Push the weather object into the forecast array.
+
+ hours.push(weather);
+ }
+
+ return hours;
+ },
+ //
+ // Determine Min and Max temp based on a supplied Forecast Element index and a boolen that denotes if
+ // the next Forecast element should be considered - i.e. look at Today *and* Tonight vs.Tonight-only
+ //
+
+ setMinMaxTemps(weather, foreGroup, today, fullDay, currentTemp) {
+ var todayTemp = foreGroup[today].querySelector("temperatures temperature").textContent;
+
+ var todayClass = foreGroup[today].querySelector("temperatures temperature").getAttribute("class");
+
+ //
+ // The following logic is largely aimed at accommodating the Current day's forecast whereby we
+ // can have either Current Today+Current Tonight or only Current Tonight.
+ //
+ // If fullDay is false, then we only have Tonight for the current day's forecast - meaning we have
+ // lost a min or max temp value for the day. Therefore, we will see if we were able to cache the the
+ // Today forecast for the current day. If we have, we will use them. If we do not have the cached values,
+ // it means that MM or the Computer has been restarted since the time EC rolled off Today from the
+ // forecast. In this scenario, we will simply default to the Current Conditions temperature and then
+ // check the Tonight temperature.
+ //
+
+ if (fullDay === false) {
+ if (this.todayCached === true) {
+ weather.minTemperature = this.todayTempCacheMin;
+ weather.maxTemperature = this.todayTempCacheMax;
+ } else {
+ weather.minTemperature = this.convertTemp(currentTemp);
+ weather.maxTemperature = weather.minTemperature;
+ }
+ }
+
+ //
+ // We will check to see if the current Element's temperature is Low or High and set weather values
+ // accordingly. We will also check the condition where fullDay is true *and* we are looking at forecast
+ // element 0. This is a special case where we will cache temperature values so that we have them later
+ // in the current day when the Current Today element rolls off and we have Current Tonight only.
+ //
+
+ if (todayClass === "low") {
+ weather.minTemperature = this.convertTemp(todayTemp);
+ if (today === 0 && fullDay === true) {
+ this.todayTempCacheMin = weather.minTemperature;
+ }
+ }
+
+ if (todayClass === "high") {
+ weather.maxTemperature = this.convertTemp(todayTemp);
+ if (today === 0 && fullDay === true) {
+ this.todayTempCacheMax = weather.maxTemperature;
+ }
+ }
+
+ var nextTemp = foreGroup[today + 1].querySelector("temperatures temperature").textContent;
+
+ var nextClass = foreGroup[today + 1].querySelector("temperatures temperature").getAttribute("class");
+
+ if (fullDay === true) {
+ if (nextClass === "low") {
+ weather.minTemperature = this.convertTemp(nextTemp);
+ }
+
+ if (nextClass === "high") {
+ weather.maxTemperature = this.convertTemp(nextTemp);
+ }
+ }
+
+ return;
+ },
+
+ //
+ // Check for a Precipitation forecast. EC can provide a forecast in 2 ways: either an accumulation figure
+ // or a POP percentage. If there is a POP, then that is what the module will show. If there is an accumulation,
+ // then it will be displayed ONLY if no POP is present.
+ //
+ // POP Logic: By default, we want to show the POP for 'daytime' since we are presuming that is what
+ // people are more interested in seeing. While EC provides a separate POP for daytime and nightime portions
+ // of each day, the weather module does not really allow for that view of a daily forecast. There we will
+ // ignore any nightime portion. There is an exception however! For the Current day, the EC data will only show
+ // the nightime forecast after a certain point in the afternoon. As such, we will be showing the nightime POP
+ // (if one exists) in that specific scenario.
+ //
+ // Accumulation Logic: Similar to POP, we want to show accumulation for 'daytime' since we presume that is what
+ // people are interested in seeing. While EC provides a separate accumulation for daytime and nightime portions
+ // of each day, the weather module does not really allow for that view of a daily forecast. There we will
+ // ignore any nightime portion. There is an exception however! For the Current day, the EC data will only show
+ // the nightime forecast after a certain point in that specific scenario.
+ //
+
+ setPrecipitation(weather, foreGroup, today) {
+ if (foreGroup[today].querySelector("precipitation accumulation")) {
+ weather.precipitationUnits = foreGroup[today].querySelector("precipitation accumulation amount").getAttribute("units");
+
+ weather.precipitation = foreGroup[today].querySelector("precipitation accumulation amount").textContent * 1.0;
+ }
+
+ // Check Today element for POP
+
+ if (foreGroup[today].querySelector("abbreviatedForecast pop").textContent > 0) {
+ weather.precipitation = foreGroup[today].querySelector("abbreviatedForecast pop").textContent;
+ weather.precipitationUnits = foreGroup[today].querySelector("abbreviatedForecast pop").getAttribute("units");
+ }
+
+ return;
+ },
+
+ //
+ // Unit conversions
+ //
+ //
+ // Convert C to F temps
+ //
+ convertTemp(temp) {
+ if (this.config.tempUnits === "imperial") {
+ return 1.8 * temp + 32;
+ } else {
+ return temp;
+ }
+ },
+ //
+ // Convert km/h to mph
+ //
+ convertWind(kilo) {
+ if (this.config.windUnits === "imperial") {
+ return kilo / 1.609344;
+ } else {
+ return kilo;
+ }
+ },
+ //
+ // Convert cm or mm to inches
+ //
+ convertPrecipAmt(amt, units) {
+ if (this.config.units === "imperial") {
+ if (units === "cm") {
+ return amt * 0.394;
+ }
+ if (units === "mm") {
+ return amt * 0.0394;
+ }
+ } else {
+ return amt;
+ }
+ },
+
+ //
+ // Convert ensure precip units accurately reflect configured units
+ //
+ convertPrecipUnits(units) {
+ if (this.config.units === "imperial") {
+ return null;
+ } else {
+ return " " + units;
+ }
+ },
+
+ //
+ // Convert the icons to a more usable name.
+ //
+ convertWeatherType(weatherType) {
+ const weatherTypes = {
+ "00": "day-sunny",
+ "01": "day-sunny",
+ "02": "day-sunny-overcast",
+ "03": "day-cloudy",
+ "04": "day-cloudy",
+ "05": "day-cloudy",
+ "06": "day-sprinkle",
+ "07": "day-showers",
+ "08": "day-snow",
+ "09": "day-thunderstorm",
+ 10: "cloud",
+ 11: "showers",
+ 12: "rain",
+ 13: "rain",
+ 14: "sleet",
+ 15: "sleet",
+ 16: "snow",
+ 17: "snow",
+ 18: "snow",
+ 19: "thunderstorm",
+ 20: "cloudy",
+ 21: "cloudy",
+ 22: "day-cloudy",
+ 23: "day-haze",
+ 24: "fog",
+ 25: "snow-wind",
+ 26: "sleet",
+ 27: "sleet",
+ 28: "rain",
+ 29: "na",
+ 30: "night-clear",
+ 31: "night-clear",
+ 32: "night-partly-cloudy",
+ 33: "night-alt-cloudy",
+ 34: "night-alt-cloudy",
+ 35: "night-partly-cloudy",
+ 36: "night-alt-showers",
+ 37: "night-rain-mix",
+ 38: "night-alt-snow",
+ 39: "night-thunderstorm",
+ 40: "snow-wind",
+ 41: "tornado",
+ 42: "tornado",
+ 43: "windy",
+ 44: "smoke",
+ 45: "sandstorm",
+ 46: "thunderstorm",
+ 47: "thunderstorm",
+ 48: "tornado"
+ };
+
+ return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null;
+ }
+});
diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js
index 3fbbb42a..5bd5be9a 100755
--- a/modules/default/weather/weatherobject.js
+++ b/modules/default/weather/weatherobject.js
@@ -28,6 +28,7 @@ class WeatherObject {
this.rain = null;
this.snow = null;
this.precipitation = null;
+ this.precipitationUnits = null;
this.feelsLikeTemp = null;
}