I have an old iPhone 4s that I wished to repurpose as a clock/nightlight. I expanded it to retrieve the weather based on the user's ip address.
A Pen by Steven Estrella on CodePen.
I have an old iPhone 4s that I wished to repurpose as a clock/nightlight. I expanded it to retrieve the weather based on the user's ip address.
A Pen by Steven Estrella on CodePen.
| <main id="container" class="daymode"> | |
| <time id="date" datetime="" class="clocktext"></time> | |
| <time id="time" datetime="" class="clocktext"></time> | |
| <div id="weather" class="clocktext infotext"></div> | |
| <img id="icon" src="https://openweathermap.org/img/w/01n.png" alt="weather icon"/> | |
| <div id="gps" class="clocktext infotext"></div> | |
| <div id="weatherdetails" class="clocktext infotext"></div> | |
| <div id="ssmlink" class="clocktext infotext"> | |
| <a href="https://shearspiremedia.com/demos/clock/" target="_blank">ShearSpire Media Weather Clock</a> | |
| </div> | |
| </main> | |
| <!-- A simple clock that fits on an old iPhone 4s so you can reuse your old device as a wall clock. This version also includes ip location detection and displays the current weather. --> |
| //NOTE: ES5 chosen instead of ES6 for compatibility with older devices | |
| var now, dd, td, details; | |
| var lat, lon, gd; | |
| var weatherurl, wd, icon; | |
| var city, region; | |
| var temperaturescale = "F"; //set to F or C (fahrenheit or celsius) | |
| var usephp = false; // set to true to use a php document to hide your api key | |
| var locationRequested = false; | |
| var weatherminute; | |
| var months = ["January","February","March","April","May","June","July","August","September","October","November","December"]; | |
| var days = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; | |
| var sunsettime = 0; | |
| var sunrisetime = 0; | |
| var iconurl = "https://openweathermap.org/img/w/"; | |
| var winddegrees = [[348.75,"N"],[326.25,"NNW"],[303.75,"NW"],[281.25,"WNW"],[258.75,"W"],[236.25,"WSW"],[213.75,"SW"],[191.25,"SSW"],[168.75,"S"],[146.25,"SSE"],[123.75,"SE"],[101.25,"ESE"],[78.75,"E"],[56.25,"ENE"],[33.75,"NE"],[11.25,"NNE"],[0,"N"]]; | |
| document.addEventListener("DOMContentLoaded", init, false); | |
| function init(){ | |
| //a set of custom icons I created | |
| iconurl = "https://shearspiremedia.com/demos/icons4owm/"; | |
| dd = document.getElementById("date"); | |
| td = document.getElementById("time"); | |
| wd = document.getElementById("weather"); | |
| gd = document.getElementById("gps"); | |
| icon = document.getElementById("icon"); | |
| details = document.getElementById("weatherdetails"); | |
| weatherminute = randRange(0,14); | |
| getLocation(); | |
| updateTime(); | |
| setInterval(updateTime,1000); | |
| } | |
| function updateTime(){ | |
| var clockdata = getClockStrings(); | |
| dd.innerHTML = clockdata.datehtml; | |
| td.innerHTML = clockdata.timehtml; | |
| dd.dateTime = now.toISOString(); | |
| td.dateTime = now.toISOString(); | |
| var sec = now.getSeconds(); | |
| var minutes = now.getMinutes(); | |
| if (locationRequested && sec === 0){ | |
| checkForSunset(); //checks for sunset once each minute | |
| if (minutes % 15 === weatherminute){ | |
| getWeather(); //get weather every 15 minutes | |
| //weatherminute is a random number between | |
| //0 and 14 to ensure that users don't all hit | |
| //the API at the same minute | |
| } | |
| } | |
| } | |
| function getClockStrings(){ | |
| now = new Date(); | |
| var year = now.getFullYear(); | |
| var month = months[now.getMonth()]; | |
| var date = now.getDate(); | |
| var day = days[now.getDay()]; | |
| var hour = now.getHours(); | |
| var minutes = now.getMinutes(); | |
| var seconds = now.getSeconds(); | |
| var meridian = hour < 12 ? "AM" : "PM"; | |
| var clockhour = hour > 12 ? hour - 12 : hour; | |
| if (hour === 0) {clockhour = 12;} | |
| var clockminutes = minutes < 10 ? "0" + minutes : minutes; | |
| var clockseconds = seconds < 10 ? "0" + seconds : seconds; | |
| var datehtml = day + ", " + month + " " + date + ", " + year; | |
| var timehtml = clockhour + ":" + clockminutes + "<span>:" + clockseconds + " " + meridian + "</span>"; | |
| return {"datehtml":datehtml,"timehtml":timehtml}; | |
| } | |
| function getLocation() { | |
| var xhttp = new XMLHttpRequest(); | |
| xhttp.onreadystatechange = function() { | |
| if (this.readyState === 4) { | |
| var noerror = true;//for testing | |
| if (this.status === 200 && noerror){ | |
| var data = xhttp.responseText; | |
| showPosition(JSON.parse(data)); | |
| }else{ | |
| showPosition(null); | |
| } | |
| } | |
| }; | |
| xhttp.open("GET", "https://extreme-ip-lookup.com/json/", true); | |
| xhttp.send(); | |
| } | |
| function showPosition(position) { | |
| if (!position){ | |
| gd.innerHTML = "IP address location service is unavailable."; | |
| return; | |
| } | |
| lat = Number(position.lat); | |
| lon = Number(position.lon); | |
| city = position.city; | |
| region = position.region; | |
| //gd.innerHTML = "GPS: " + lat.toFixed(2) + " | " + lon.toFixed(2); | |
| gd.innerHTML = city + ", " + region; | |
| if (usephp){ | |
| weatherurl = "clock.php?lat=" + lat + "&lon=" + lon; | |
| //weatherurl = "clock.php?lat=200&lon=200"; // for testing error response | |
| }else{ | |
| weatherurl = "https://api.openweathermap.org/data/2.5/weather?"; | |
| weatherurl += "lat=" + lat + "&lon=" + lon + "&APPID="; | |
| weatherurl += YOUR_API_KEY_HERE; | |
| //for the APPID, please substitute your own API Key you can get for free from openweathermap.org | |
| } | |
| /* | |
| an alternative to exposing your API Key is to call a PHP document | |
| where the API Key is stored securely on the server. The PHP document in turn | |
| calls the weatherurl and returns an HTML document whose body is a json string that | |
| can be parsed. Codepen doesn't allow php access so I established a throw-away | |
| account on openweathermap.org for this demonstration which has the apikey referenced here. | |
| for a working example that uses PHP to hide the api key see | |
| https://shearspiremedia.com/demos/clock/ | |
| */ | |
| if (!locationRequested){ | |
| getWeather(); | |
| locationRequested = true; | |
| } | |
| } | |
| function getWeather(){ | |
| wd.innerHTML = "getting weather"; | |
| // I opted to use the older XMLHttpRequest because fetch is not supported on old devices like the iPhone 4s | |
| // I developed this page so I could use my old iPhone 4s as a wall clock. | |
| var xhttp = new XMLHttpRequest(); | |
| xhttp.responseType = usephp ? "document" : "text"; //the php file returns a document rather than plain text | |
| xhttp.onreadystatechange = function() { | |
| if (this.readyState === 4 && this.status === 200) { | |
| //when using PHP as a data source we need the textContent of the body of the returned document | |
| var data = usephp ? xhttp.response.body.textContent : xhttp.responseText; | |
| processWeather(JSON.parse(data)); | |
| } | |
| }; | |
| xhttp.open("GET", weatherurl, true); | |
| xhttp.send(); | |
| } | |
| function convertTemperature(kelvin){ | |
| //converts temps in kelvin to celsius or fahrenheit | |
| var celsius = (kelvin - 273.15); | |
| return temperaturescale === "F" ? celsius * 1.8 + 32 : celsius; | |
| } | |
| function processWeather(data){ | |
| var weather = data["weather"][0]; | |
| icon.src = iconurl + weather.icon + ".png"; | |
| icon.style.opacity = 1; | |
| var localtemperature = convertTemperature(data["main"].temp).toFixed(0); | |
| wd.innerHTML = localtemperature + "°" + temperaturescale + " " + weather.description; | |
| sunsettime = Number(data["sys"].sunset); | |
| sunrisetime = Number(data["sys"].sunrise); | |
| checkForSunset(); | |
| var gpsline = "GPS: " + lat.toFixed(2) + " | " + lon.toFixed(2) + "<br>"; | |
| var pressureline = "Pressure: " + data.main.pressure + " hpa<br>"; | |
| var humidityline = "Humidity: " + data.main.humidity + "%<br>"; | |
| var windline = "Winds: " + data.wind.speed + " mph "; | |
| if (data.wind.deg){ | |
| windline += Math.round(data.wind.deg) + "°(" + getWindDirection(data.wind.deg) + ")"; | |
| } | |
| windline += "<br>"; | |
| var sunriseline = "Sunrise: " + new Date(data.sys.sunrise * 1000).toLocaleTimeString() + "<br>"; | |
| var sunsetline = "Sunset: " + new Date(data.sys.sunset * 1000).toLocaleTimeString() + "<br>"; | |
| details.innerHTML = windline + pressureline + humidityline + sunriseline + sunsetline + gpsline; | |
| } | |
| function checkForSunset(){ | |
| var nowtime = now.getTime()/1000; | |
| //changes the presentation style if the time of day is after sunset | |
| //or before the next day's sunrise | |
| var isDark = nowtime > sunsettime || nowtime < sunrisetime; | |
| document.getElementById("container").className = isDark ? "nightmode" : "daymode"; | |
| //uncomment the following if you want santa mode | |
| // if (now.getMonth() === 11 && now.getDate() < 26){ | |
| // document.getElementById("container").className = "santamode"; | |
| // } | |
| } | |
| //random number utility function | |
| function randRange(min, max) { | |
| return Math.floor(Math.random()*(max-min+1))+min; | |
| } | |
| function getWindDirection(deg){ | |
| for (var i=0;i<winddegrees.length;i++){ | |
| if (deg > winddegrees[i][0]){ | |
| return winddegrees[i][1]; | |
| } | |
| } | |
| return "__"; | |
| } | |
| <script src="https://shearspiremedia.com/demos/clock/yak.js"></script> |
| html {font-size:16px;} | |
| body { | |
| padding:0; | |
| margin:0; | |
| font-family:TrebuchetMS,Arial,sans-serif; | |
| } | |
| #container { | |
| margin:0 auto; | |
| padding:0; | |
| width:100vw; | |
| height:100vh; | |
| text-align:center; | |
| overflow:auto; | |
| } | |
| .nightmode { | |
| background-color:#121212; | |
| background-image: linear-gradient(to bottom left, #121212 10%,#333955 100%); | |
| color:#fff; | |
| text-shadow:1px 1px 1px black; | |
| } | |
| .daymode { | |
| background-color: #87ceeb; | |
| background-image: linear-gradient(to bottom left, #87ceeb 0%,#fff 100%); | |
| color:#333; | |
| text-shadow:1px 1px 10px white; | |
| } | |
| .santamode { | |
| background-color: #0f3; | |
| background-image: linear-gradient(to bottom left, #0f3 0%,#fff 100%); | |
| color:#c00; | |
| text-shadow:1px 1px 10px white; | |
| } | |
| .clocktext { | |
| display:block; | |
| margin:0; | |
| padding:1px 0 0 0; | |
| width:100%; | |
| text-align:center; | |
| line-height:1.0; | |
| white-space:nowrap; | |
| } | |
| #date { | |
| font-size:1.3rem; | |
| padding-top:15px; | |
| } | |
| #time { | |
| font-size:5rem; | |
| margin:1px 0 0 0; | |
| } | |
| #time span { | |
| display:inline-block; | |
| padding:0; | |
| vertical-align: baseline; | |
| text-align:left; | |
| width: 2em; | |
| margin:0 0 0 0.5em; | |
| font-size:1.5rem; | |
| line-height:1.5; | |
| white-space:normal; | |
| } | |
| .infotext { | |
| margin:0; | |
| padding:0 5px 0 5px; | |
| font-size:1.3rem; | |
| line-height:1.4; | |
| width:auto; | |
| } | |
| #weather {display:block;width:auto;} | |
| #weatherdetails {margin:50px 0 20px 0;} | |
| #icon { | |
| display:inline-block; | |
| opacity:0; | |
| vertical-align:top; | |
| height:50px;width:50px; | |
| } | |
| #gpsbutton { | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| display:block; | |
| margin:0 auto; | |
| padding:0 16px 0 16px; | |
| width:auto; | |
| height:40px; | |
| border:2px outset #fff; | |
| border-radius:12px; | |
| background:#dedeff; | |
| color:black; | |
| font-size:20px; | |
| cursor:pointer; | |
| } | |
| #gpsbutton:hover { | |
| background:#000033; | |
| border:2px inset #fff; | |
| color:#fff; | |
| } | |
| @media (min-width: 480px){ | |
| #date {font-size:2rem;} | |
| #time {font-size:8rem;} | |
| #time span { | |
| font-size:2rem; | |
| line-height: 2; | |
| } | |
| .infotext { | |
| font-size:1.8rem; | |
| } | |
| #weather {display:inline-block;} | |
| } | |
| @media (min-width: 1000px){ | |
| #icon {height:100px;width:100px;} | |
| #date {font-size:4rem;} | |
| #time {font-size:16rem;} | |
| #time span { | |
| font-size:4rem; | |
| line-height: 2; | |
| } | |
| .infotext { | |
| font-size:3.6rem; | |
| } | |
| #weather {display:inline-block;} | |
| } | |
| #ssmlink {margin-bottom:30px;} | |
| #ssmlink a:link, #ssmlink a:visited {color:#090;font-size:80%;} | |
| #ssmlink a:hover {color:#0f0;} |