diff --git a/.env b/.env index 6f59eb4..608ccc5 100644 --- a/.env +++ b/.env @@ -3,6 +3,6 @@ # collaborators will be able to see your .env values # reference these in your code with process.env.SECRET_NAME -DARKSKY_API_KEY="_PUT_YOUR_DARK_SKY_API_KEY_HERE_" +OPEN_WEATHER_MAP_API_KEY="_PUT_YOUR_OPEN_WEATHER_MAP_API_KEY_HERE_" # note: .env is a shell file so there can't be spaces around = diff --git a/package.json b/package.json index d881c81..e980e2f 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "node server.js" }, "dependencies": { + "dotenv": "^8.2.0", "express": "^4.16.4", "express-http-to-https": "^1.1.4", "node-fetch": "^2.3.0" diff --git a/public/images/01d.png b/public/images/01d.png new file mode 100644 index 0000000..ed42ad9 Binary files /dev/null and b/public/images/01d.png differ diff --git a/public/images/01n.png b/public/images/01n.png new file mode 100644 index 0000000..85efa16 Binary files /dev/null and b/public/images/01n.png differ diff --git a/public/images/02d.png b/public/images/02d.png new file mode 100644 index 0000000..fabd9c3 Binary files /dev/null and b/public/images/02d.png differ diff --git a/public/images/02n.png b/public/images/02n.png new file mode 100644 index 0000000..288a40e Binary files /dev/null and b/public/images/02n.png differ diff --git a/public/images/03d.png b/public/images/03d.png new file mode 100644 index 0000000..ef2e9f7 Binary files /dev/null and b/public/images/03d.png differ diff --git a/public/images/03n.png b/public/images/03n.png new file mode 100644 index 0000000..ef2e9f7 Binary files /dev/null and b/public/images/03n.png differ diff --git a/public/images/04d.png b/public/images/04d.png new file mode 100644 index 0000000..9c64ea8 Binary files /dev/null and b/public/images/04d.png differ diff --git a/public/images/04n.png b/public/images/04n.png new file mode 100644 index 0000000..9c64ea8 Binary files /dev/null and b/public/images/04n.png differ diff --git a/public/images/09d.png b/public/images/09d.png new file mode 100644 index 0000000..0f14cb6 Binary files /dev/null and b/public/images/09d.png differ diff --git a/public/images/09n.png b/public/images/09n.png new file mode 100644 index 0000000..0f14cb6 Binary files /dev/null and b/public/images/09n.png differ diff --git a/public/images/10d.png b/public/images/10d.png new file mode 100644 index 0000000..62304fd Binary files /dev/null and b/public/images/10d.png differ diff --git a/public/images/10n.png b/public/images/10n.png new file mode 100644 index 0000000..b5e5d10 Binary files /dev/null and b/public/images/10n.png differ diff --git a/public/images/11d.png b/public/images/11d.png new file mode 100644 index 0000000..4a885cf Binary files /dev/null and b/public/images/11d.png differ diff --git a/public/images/11n.png b/public/images/11n.png new file mode 100644 index 0000000..4a885cf Binary files /dev/null and b/public/images/11n.png differ diff --git a/public/images/13d.png b/public/images/13d.png new file mode 100644 index 0000000..7867322 Binary files /dev/null and b/public/images/13d.png differ diff --git a/public/images/13n.png b/public/images/13n.png new file mode 100644 index 0000000..7867322 Binary files /dev/null and b/public/images/13n.png differ diff --git a/public/images/50d.png b/public/images/50d.png new file mode 100644 index 0000000..f04122b Binary files /dev/null and b/public/images/50d.png differ diff --git a/public/images/50n.png b/public/images/50n.png new file mode 100644 index 0000000..f04122b Binary files /dev/null and b/public/images/50n.png differ diff --git a/public/images/clear-day.svg b/public/images/clear-day.svg deleted file mode 100644 index 7197f0f..0000000 --- a/public/images/clear-day.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/clear-night.svg b/public/images/clear-night.svg deleted file mode 100644 index aa663b3..0000000 --- a/public/images/clear-night.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/cloudy.svg b/public/images/cloudy.svg deleted file mode 100644 index b78362c..0000000 --- a/public/images/cloudy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/fog.svg b/public/images/fog.svg deleted file mode 100644 index b1d7700..0000000 --- a/public/images/fog.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/hail.svg b/public/images/hail.svg deleted file mode 100644 index 019e389..0000000 --- a/public/images/hail.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/partly-cloudy-day.svg b/public/images/partly-cloudy-day.svg deleted file mode 100644 index 5df06a5..0000000 --- a/public/images/partly-cloudy-day.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/partly-cloudy-night.svg b/public/images/partly-cloudy-night.svg deleted file mode 100644 index af9c791..0000000 --- a/public/images/partly-cloudy-night.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/rain.svg b/public/images/rain.svg deleted file mode 100644 index 308bab7..0000000 --- a/public/images/rain.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/sleet.svg b/public/images/sleet.svg deleted file mode 100644 index a374151..0000000 --- a/public/images/sleet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/snow.svg b/public/images/snow.svg deleted file mode 100644 index 0e76792..0000000 --- a/public/images/snow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/thunderstorm.svg b/public/images/thunderstorm.svg deleted file mode 100644 index 608a7ca..0000000 --- a/public/images/thunderstorm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/tornado.svg b/public/images/tornado.svg deleted file mode 100644 index 7bc1fb3..0000000 --- a/public/images/tornado.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/images/wind.svg b/public/images/wind.svg deleted file mode 100644 index d613313..0000000 --- a/public/images/wind.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/index.html b/public/index.html index 1eee1c1..a5bd0f3 100644 --- a/public/index.html +++ b/public/index.html @@ -38,8 +38,8 @@

Weather PWA - - Powered by Dark Sky + + Powered by Open Weather Map

diff --git a/public/offline.html b/public/offline.html index fc425b6..604f4e6 100644 --- a/public/offline.html +++ b/public/offline.html @@ -129,8 +129,8 @@

Weather PWA - - Powered by Dark Sky + + Powered by Open Weather Map

diff --git a/public/scripts/app.js b/public/scripts/app.js index 234fab2..8acb98a 100644 --- a/public/scripts/app.js +++ b/public/scripts/app.js @@ -29,6 +29,13 @@ function toggleAddDialog() { weatherApp.addDialogContainer.classList.toggle('visible'); } +/** + * Capitalize first letter. + */ +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + /** * Event handler for butDialogAdd, adds the selected location to the list. */ @@ -83,35 +90,34 @@ function renderForecast(card, data) { const lastUpdated = parseInt(cardLastUpdated); // If the data on the element is newer, skip the update. - if (lastUpdated >= data.currently.time) { + if (lastUpdated >= data.current.dt) { return; } - cardLastUpdatedElem.textContent = data.currently.time; - + cardLastUpdatedElem.textContent = data.current.dt; // Render the forecast data into the card. - card.querySelector('.description').textContent = data.currently.summary; + card.querySelector('.description').textContent = capitalizeFirstLetter(data.current.weather[0].description); const forecastFrom = luxon.DateTime - .fromSeconds(data.currently.time) + .fromSeconds(data.current.dt) .setZone(data.timezone) .toFormat('DDDD t'); card.querySelector('.date').textContent = forecastFrom; card.querySelector('.current .icon') - .className = `icon ${data.currently.icon}`; + .className = `icon i${data.current.weather[0].icon}`; card.querySelector('.current .temperature .value') - .textContent = Math.round(data.currently.temperature); + .textContent = Math.round(data.current.temp); card.querySelector('.current .humidity .value') - .textContent = Math.round(data.currently.humidity * 100); + .textContent = Math.round(data.current.humidity); card.querySelector('.current .wind .value') - .textContent = Math.round(data.currently.windSpeed); + .textContent = Math.round(data.current.wind_speed); card.querySelector('.current .wind .direction') - .textContent = Math.round(data.currently.windBearing); + .textContent = Math.round(data.current.wind_deg); const sunrise = luxon.DateTime - .fromSeconds(data.daily.data[0].sunriseTime) + .fromSeconds(data.current.sunrise) .setZone(data.timezone) .toFormat('t'); card.querySelector('.current .sunrise .value').textContent = sunrise; const sunset = luxon.DateTime - .fromSeconds(data.daily.data[0].sunsetTime) + .fromSeconds(data.current.sunset) .setZone(data.timezone) .toFormat('t'); card.querySelector('.current .sunset .value').textContent = sunset; @@ -119,17 +125,17 @@ function renderForecast(card, data) { // Render the next 7 days. const futureTiles = card.querySelectorAll('.future .oneday'); futureTiles.forEach((tile, index) => { - const forecast = data.daily.data[index + 1]; + const forecast = data.daily[index + 1]; const forecastFor = luxon.DateTime - .fromSeconds(forecast.time) + .fromSeconds(forecast.dt) .setZone(data.timezone) .toFormat('ccc'); tile.querySelector('.date').textContent = forecastFor; - tile.querySelector('.icon').className = `icon ${forecast.icon}`; + tile.querySelector('.icon').className = `icon i${forecast.weather[0].icon}`; tile.querySelector('.temp-high .value') - .textContent = Math.round(forecast.temperatureHigh); + .textContent = Math.round(forecast.temp.max); tile.querySelector('.temp-low .value') - .textContent = Math.round(forecast.temperatureLow); + .textContent = Math.round(forecast.temp.min); }); // If the loading spinner is still visible, remove it. diff --git a/public/styles/inline.css b/public/styles/inline.css index 6580dba..df79f14 100644 --- a/public/styles/inline.css +++ b/public/styles/inline.css @@ -171,56 +171,76 @@ body { background-image: url("/images/add.svg"); } -.icon.clear-day { - background-image: url("/images/clear-day.svg"); +.icon.i01d { + background-image: url("/images/01d.png"); } -.icon.clear-night { - background-image: url("/images/clear-night.svg"); +.icon.i01n { + background-image: url("/images/01n.png"); } -.icon.rain { - background-image: url("/images/rain.svg"); +.icon.i02d { + background-image: url("/images/02d.png"); } -.icon.snow { - background-image: url("/images/snow.svg"); +.icon.i02n { + background-image: url("/images/02n.png"); } -.icon.sleet { - background-image: url("/images/sleet.svg"); +.icon.i03d { + background-image: url("/images/03d.png"); } -.icon.wind { - background-image: url("/images/wind.svg"); +.icon.i03n { + background-image: url("/images/03n.png"); } -.icon.fog { - background-image: url("/images/fog.svg"); +.icon.i04d { + background-image: url("/images/04d.png"); } -.icon.cloudy { - background-image: url("/images/cloudy.svg"); +.icon.i04n { + background-image: url("/images/04n.png"); } -.icon.partly-cloudy-day { - background-image: url("/images/partly-cloudy-day.svg"); +.icon.i09d { + background-image: url("/images/09d.png"); } -.icon.partly-cloudy-night { - background-image: url("/images/partly-cloudy-night.svg"); +.icon.i09n { + background-image: url("/images/09n.png"); } -.icon.hail { - background-image: url("/images/hail.svg"); +.icon.i10d { + background-image: url("/images/10d.png"); } -.icon.thunderstorm { - background-image: url("/images/thunderstorm.svg"); +.icon.i10n { + background-image: url("/images/10n.png"); } -.icon.tornado { - background-image: url("/images/tornado.svg"); +.icon.i11d { + background-image: url("/images/11d.png"); +} + +.icon.i11n { + background-image: url("/images/11n.png"); +} + +.icon.i13d { + background-image: url("/images/13d.png"); +} + +.icon.i13n { + background-image: url("/images/13n.png"); +} + +.icon.i50d { + background-image: url("/images/50d.png"); +} + +.icon.i50n { + background-image: url("/images/50n.png"); } /** diff --git a/server.js b/server.js index 4d747b7..a02eceb 100644 --- a/server.js +++ b/server.js @@ -16,102 +16,121 @@ * See the License for the specific language governing permissions and * limitations under the License */ -'use strict'; +"use strict"; -const express = require('express'); -const fetch = require('node-fetch'); -const redirectToHTTPS = require('express-http-to-https').redirectToHTTPS; +require('dotenv').config() +const express = require("express"); +const fetch = require("node-fetch"); +const redirectToHTTPS = require("express-http-to-https").redirectToHTTPS; // CODELAB: Change this to add a delay (ms) before the server responds. const FORECAST_DELAY = 0; -// CODELAB: If running locally, set your Dark Sky API key here -const API_KEY = process.env.DARKSKY_API_KEY; -const BASE_URL = `https://api.darksky.net/forecast`; +// CODELAB: If running locally, set your Open Weather Map API key here +const API_KEY = process.env.OPEN_WEATHER_MAP_API_KEY; +console.log(API_KEY); +const BASE_URL = `https://api.openweathermap.org/data/2.5/onecall`; -// Fake forecast data used if we can't reach the Dark Sky API +// Fake forecast data used if we can't reach the Open Weather Map API const fakeForecast = { fakeData: true, latitude: 0, longitude: 0, - timezone: 'America/New_York', - currently: { - time: 0, - summary: 'Clear', - icon: 'clear-day', - temperature: 43.4, - humidity: 0.62, - windSpeed: 3.74, - windBearing: 208, + timezone: "America/New_York", + current: { + dt: 0, + sunrise: 1587463629, + sunset: 1587512484, + temp: 43.4, + humidity: 62, + wind_speed: 3.74, + wind_deg: 208, + weather: [{ + description: "clear sky", + icon: "01d" + }] }, - daily: { - data: [ - { - time: 0, - icon: 'partly-cloudy-night', - sunriseTime: 1553079633, - sunsetTime: 1553123320, - temperatureHigh: 52.91, - temperatureLow: 41.35, + daily: [{ + dt: 0, + temp: { + min: 52.91, + max: 41.35, }, - { - time: 86400, - icon: 'rain', - sunriseTime: 1553165933, - sunsetTime: 1553209784, - temperatureHigh: 48.01, - temperatureLow: 44.17, + weather: [{ + icon: "04n" + }], + }, + { + dt: 86400, + temp: { + min: 48.01, + max: 44.17, }, - { - time: 172800, - icon: 'rain', - sunriseTime: 1553252232, - sunsetTime: 1553296247, - temperatureHigh: 50.31, - temperatureLow: 33.61, + weather: [{ + icon: "10d" + }], + }, + { + dt: 172800, + temp: { + min: 50.31, + max: 33.61, }, - { - time: 259200, - icon: 'partly-cloudy-night', - sunriseTime: 1553338532, - sunsetTime: 1553382710, - temperatureHigh: 46.44, - temperatureLow: 33.82, + weather: [{ + icon: "10d" + }], + }, + { + dt: 259200, + temp: { + min: 46.44, + max: 33.82, }, - { - time: 345600, - icon: 'partly-cloudy-night', - sunriseTime: 1553424831, - sunsetTime: 1553469172, - temperatureHigh: 60.5, - temperatureLow: 43.82, + weather: [{ + icon: "04n" + }], + }, + { + dt: 345600, + temp: { + min: 60.5, + max: 43.82, }, - { - time: 432000, - icon: 'rain', - sunriseTime: 1553511130, - sunsetTime: 1553555635, - temperatureHigh: 61.79, - temperatureLow: 32.8, + weather: [{ + icon: "04n" + }], + }, + { + dt: 432000, + temp: { + min: 61.79, + max: 32.8, }, - { - time: 518400, - icon: 'rain', - sunriseTime: 1553597430, - sunsetTime: 1553642098, - temperatureHigh: 48.28, - temperatureLow: 33.49, + weather: [{ + icon: "10d" + }], + }, + { + dt: 518400, + temp: { + min: 48.28, + max: 33.49, }, - { - time: 604800, - icon: 'snow', - sunriseTime: 1553683730, - sunsetTime: 1553728560, - temperatureHigh: 43.58, - temperatureLow: 33.68, + weather: [{ + icon: "10d" + }], + }, + { + dt: 604800, + temp: { + min: 48.58, + max: 33.68, }, - ], - }, + weather: [{ + icon: "13d" + }], + }, + ] }; /** @@ -121,8 +140,8 @@ const fakeForecast = { * @return {Object} forecast object. */ function generateFakeForecast(location) { - location = location || '40.7720232,-73.9732319'; - const commaAt = location.indexOf(','); + location = location || "40.7720232,-73.9732319"; + const commaAt = location.indexOf(","); // Create a new copy of the forecast const result = Object.assign({}, fakeForecast); @@ -131,29 +150,35 @@ function generateFakeForecast(location) { return result; } - /** - * Gets the weather forecast from the Dark Sky API for the given location. + * Gets the weather forecast from the Open Weather Map API for the given location. * * @param {Request} req request object from Express. * @param {Response} resp response object from Express. */ function getForecast(req, resp) { - const location = req.params.location || '40.7720232,-73.9732319'; - const url = `${BASE_URL}/${API_KEY}/${location}`; - fetch(url).then((resp) => { - if (resp.status !== 200) { - throw new Error(resp.statusText); - } - return resp.json(); - }).then((data) => { - setTimeout(() => { - resp.json(data); - }, FORECAST_DELAY); - }).catch((err) => { - console.error('Dark Sky API Error:', err.message); - resp.json(generateFakeForecast(location)); - }); + const location = req.params.location || "40.7720232,-73.9732319"; + const commaAt = location.indexOf(","); + const latitude = parseFloat(location.substr(0, commaAt)); + const longitude = parseFloat(location.substr(commaAt + 1)); + const url = `${BASE_URL}?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=imperial`; + console.log(url); + fetch(url) + .then(resp => { + if (resp.status !== 200) { + throw new Error(resp.statusText); + } + return resp.json(); + }) + .then(data => { + setTimeout(() => { + resp.json(data); + }, FORECAST_DELAY); + }) + .catch(err => { + console.error("Open Weather Map API Error:", err.message); + resp.json(generateFakeForecast(location)); + }); } /** @@ -179,17 +204,17 @@ function startServer() { }); // Handle requests for the data - app.get('/forecast/:location', getForecast); - app.get('/forecast/', getForecast); - app.get('/forecast', getForecast); + app.get("/forecast/:location", getForecast); + app.get("/forecast/", getForecast); + app.get("/forecast", getForecast); // Handle requests for static files - app.use(express.static('public')); + app.use(express.static("public")); // Start the server - return app.listen('8000', () => { + return app.listen("8000", () => { // eslint-disable-next-line no-console - console.log('Local DevServer Started on port 8000...'); + console.log("Local DevServer Started on port 8000..."); }); }