diff --git a/assets/Cloud.png b/assets/Cloud.png new file mode 100644 index 00000000..32e346d3 Binary files /dev/null and b/assets/Cloud.png differ diff --git a/assets/Hail.png b/assets/Hail.png new file mode 100644 index 00000000..be68fea5 Binary files /dev/null and b/assets/Hail.png differ diff --git a/assets/Haze.png b/assets/Haze.png new file mode 100755 index 00000000..e790d20d Binary files /dev/null and b/assets/Haze.png differ diff --git a/assets/Moon.png b/assets/Moon.png new file mode 100644 index 00000000..f0b2d0e4 Binary files /dev/null and b/assets/Moon.png differ diff --git a/assets/Newspaper.png b/assets/Newspaper.png new file mode 100644 index 00000000..d09791cc Binary files /dev/null and b/assets/Newspaper.png differ diff --git a/assets/PartlyMoon.png b/assets/PartlyMoon.png new file mode 100644 index 00000000..97bb5227 Binary files /dev/null and b/assets/PartlyMoon.png differ diff --git a/assets/PartlySunny.png b/assets/PartlySunny.png new file mode 100644 index 00000000..3861cff8 Binary files /dev/null and b/assets/PartlySunny.png differ diff --git a/assets/Rain.png b/assets/Rain.png new file mode 100644 index 00000000..dc7c425a Binary files /dev/null and b/assets/Rain.png differ diff --git a/assets/Snow.png b/assets/Snow.png new file mode 100644 index 00000000..ff7c2ced Binary files /dev/null and b/assets/Snow.png differ diff --git a/assets/Storm.png b/assets/Storm.png new file mode 100644 index 00000000..23d492fe Binary files /dev/null and b/assets/Storm.png differ diff --git a/assets/Sun.png b/assets/Sun.png new file mode 100644 index 00000000..f705c027 Binary files /dev/null and b/assets/Sun.png differ diff --git a/assets/Sunrise.png b/assets/Sunrise.png new file mode 100644 index 00000000..4417d80c Binary files /dev/null and b/assets/Sunrise.png differ diff --git a/assets/Tornado.png b/assets/Tornado.png new file mode 100644 index 00000000..6631a91e Binary files /dev/null and b/assets/Tornado.png differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..f8f2fef5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +requests +feedparser +Pillow diff --git a/smartmirror.py b/smartmirror.py new file mode 100644 index 00000000..fda920e5 --- /dev/null +++ b/smartmirror.py @@ -0,0 +1,276 @@ +# smartmirror.py +# requirements +# requests, feedparser, traceback, Pillow + +from Tkinter import * +import time +import requests +import json +import traceback +import feedparser +from PIL import Image, ImageTk + +ip = '' +country_code = 'us' +weather_api_token = '' + + +# maps open weather icons to +icon_lookup = { + 'clear-day': "assets/Sun.png", # clear sky day + 'wind': "assets/Wind.png", #wind + 'cloudy': "assets/Cloud.png", # cloudy day + 'partly-cloudy-day': "assets/PartlySunny.png", # partly cloudy day + 'rain': "assets/Rain.png", # rain day + 'snow': "assets/Snow.png", # snow day + 'snow-thin': "assets/Snow.png", # sleet day + 'fog': "assets/Haze.png", # fog day + 'clear-night': "assets/Moon.png", # clear sky night + 'partly-cloudy-night': "assets/PartlyMoon.png", # scattered clouds night + 'thunderstorm': "assets/Storm.png", # thunderstorm + 'tornado': "assests/Tornado.png", # tornado + 'hail': "assests/Hail.png" # hail +} + + +class Clock(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent, bg='black') + # initialize time label + self.time1 = '' + self.timeLbl = Label(self, font=('Helvetica', 48), fg="white", bg="black") + self.timeLbl.pack(side=TOP, anchor=E) + # initialize day of week + self.day_of_week1 = '' + self.dayOWLbl = Label(self, text=self.day_of_week1, font=('Helvetica', 18), fg="white", bg="black") + self.dayOWLbl.pack(side=TOP, anchor=E) + # initialize date label + self.date1 = '' + self.dateLbl = Label(self, text=self.date1, font=('Helvetica', 18), fg="white", bg="black") + self.dateLbl.pack(side=TOP, anchor=E) + self.tick() + + def tick(self): + time2 = time.strftime('%I:%M') + day_of_week2 = time.strftime('%A') + date2 = time.strftime("%b %d, %Y") + # if time string has changed, update it + if time2 != self.time1: + self.time1 = time2 + self.timeLbl.config(text=time2) + if day_of_week2 != self.day_of_week1: + self.day_of_week1 = day_of_week2 + self.dayOWLbl.config(text=day_of_week2) + if date2 != self.date1: + self.date1 = date2 + self.dateLbl.config(text=date2) + # calls itself every 200 milliseconds + # to update the time display as needed + # could use >200 ms, but display gets jerky + self.timeLbl.after(200, self.tick) + + +class Weather(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent, bg='black') + self.temperature = '' + self.forecast = '' + self.location = '' + self.currently = '' + self.icon = '' + self.degreeFrm = Frame(self, bg="black") + self.degreeFrm.pack(side=TOP, anchor=W) + self.temperatureLbl = Label(self.degreeFrm, font=('Helvetica', 94), fg="white", bg="black") + self.temperatureLbl.pack(side=LEFT, anchor=N) + self.iconLbl = Label(self.degreeFrm, bg="black") + self.iconLbl.pack(side=LEFT, anchor=N, padx=20) + self.currentlyLbl = Label(self, font=('Helvetica', 28), fg="white", bg="black") + self.currentlyLbl.pack(side=TOP, anchor=W) + self.forecastLbl = Label(self, font=('Helvetica', 18), fg="white", bg="black") + self.forecastLbl.pack(side=TOP, anchor=W) + self.locationLbl = Label(self, font=('Helvetica', 18), fg="white", bg="black") + self.locationLbl.pack(side=TOP, anchor=W) + self.get_weather() + + def get_weather(self): + try: + # get location + location_req_url = "http://freegeoip.net/json/%s" % ip + r = requests.get(location_req_url) + location_obj = json.loads(r.text) + + lat = location_obj['latitude'] + lon = location_obj['longitude'] + + location2 = "%s, %s" % (location_obj['city'], location_obj['region_code']) + + # get weather + weather_req_url = "https://api.forecast.io/forecast/%s/%s,%s" % (weather_api_token, lat,lon) + r = requests.get(weather_req_url) + weather_obj = json.loads(r.text) + + degree_sign= u'\N{DEGREE SIGN}' + temperature2 = "%s%s" % (str(int(weather_obj['currently']['temperature'])), degree_sign) + currently2 = weather_obj['currently']['summary'] + forecast2 = weather_obj["hourly"]["summary"] + + icon_id = weather_obj['currently']['icon'] + icon2 = None + + if icon_id in icon_lookup: + icon2 = icon_lookup[icon_id] + + if icon2 is not None: + if self.icon != icon2: + self.icon = icon2 + image = Image.open(icon2) + image = image.resize((100, 100), Image.ANTIALIAS) + image = image.convert('RGB') + photo = ImageTk.PhotoImage(image) + + self.iconLbl.config(image=photo) + self.iconLbl.image = photo + else: + # remove image + self.iconLbl.config(image='') + + if self.currently != currently2: + self.currently = currently2 + self.currentlyLbl.config(text=currently2) + if self.forecast != forecast2: + self.forecast = forecast2 + self.forecastLbl.config(text=forecast2) + if self.temperature != temperature2: + self.temperature = temperature2 + self.temperatureLbl.config(text=temperature2) + if self.location != location2: + self.location = location2 + self.locationLbl.config(text=location2) + except Exception as e: + traceback.print_exc() + print "Error: %s. Cannot get weather." % e + + self.after(600000, self.get_weather) + + @staticmethod + def convert_kelvin_to_fahrenheit(kelvin_temp): + return 1.8 * (kelvin_temp - 273) + 32 + + +class News(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent, *args, **kwargs) + self.config(bg='black') + self.title = 'Headlines' + self.newsLbl = Label(self, text=self.title, font=('Helvetica', 28), fg="white", bg="black") + self.newsLbl.pack(side=TOP, anchor=W) + self.headlinesContainer = Frame(self, bg="black") + self.headlinesContainer.pack(side=TOP) + self.get_headlines() + + def get_headlines(self): + try: + # remove all children + for widget in self.headlinesContainer.winfo_children(): + widget.destroy() + + headlines_url = "https://news.google.com/news?ned=%s&output=rss" % country_code + feed = feedparser.parse(headlines_url) + + for post in feed.entries[0:5]: + headline = NewsHeadline(self.headlinesContainer, post.title) + headline.pack(side=TOP, anchor=W) + except Exception as e: + traceback.print_exc() + print "Error: %s. Cannot get news." % e + + self.after(600000, self.get_headlines) + + +class NewsHeadline(Frame): + def __init__(self, parent, event_name=""): + Frame.__init__(self, parent, bg='black') + + image = Image.open("assets/Newspaper.png") + image = image.resize((25, 25), Image.ANTIALIAS) + image = image.convert('RGB') + photo = ImageTk.PhotoImage(image) + + self.iconLbl = Label(self, bg='black', image=photo) + self.iconLbl.image = photo + self.iconLbl.pack(side=LEFT, anchor=N) + + self.eventName = event_name + self.eventNameLbl = Label(self, text=self.eventName, font=('Helvetica', 18), fg="white", bg="black") + self.eventNameLbl.pack(side=LEFT, anchor=N) + + +class Calendar(Frame): + def __init__(self, parent, *args, **kwargs): + Frame.__init__(self, parent, bg='black') + self.title = 'Calendar Events' + self.calendarLbl = Label(self, text=self.title, font=('Helvetica', 28), fg="white", bg="black") + self.calendarLbl.pack(side=TOP, anchor=E) + self.calendarEventContainer = Frame(self, bg='black') + self.calendarEventContainer.pack(side=TOP, anchor=E) + self.get_events() + + def get_events(self): + #TODO: implement this method + # reference https://developers.google.com/google-apps/calendar/quickstart/python + + # remove all children + for widget in self.calendarEventContainer.winfo_children(): + widget.destroy() + + calendar_event = CalendarEvent(self.calendarEventContainer) + calendar_event.pack(side=TOP, anchor=E) + pass + + +class CalendarEvent(Frame): + def __init__(self, parent, event_name="Event 1"): + Frame.__init__(self, parent, bg='black') + self.eventName = event_name + self.eventNameLbl = Label(self, text=self.eventName, font=('Helvetica', 18), fg="white", bg="black") + self.eventNameLbl.pack(side=TOP, anchor=E) + + +class FullscreenWindow: + + def __init__(self): + self.tk = Tk() + self.tk.configure(background='black') + self.topFrame = Frame(self.tk, background = 'black') + self.bottomFrame = Frame(self.tk, background = 'black') + self.topFrame.pack(side = TOP, fill=BOTH, expand = YES) + self.bottomFrame.pack(side = BOTTOM, fill=BOTH, expand = YES) + self.state = False + self.tk.bind("", self.toggle_fullscreen) + self.tk.bind("", self.end_fullscreen) + # clock + self.clock = Clock(self.topFrame) + self.clock.pack(side=RIGHT, anchor=N, padx=100, pady=60) + # weather + self.weather = Weather(self.topFrame) + self.weather.pack(side=LEFT, anchor=N, padx=100, pady=60) + # news + self.news = News(self.bottomFrame) + self.news.pack(side=LEFT, anchor=S, padx=100, pady=60) + # calender - removing for now + # self.calender = Calendar(self.bottomFrame) + # self.calender.pack(side = RIGHT, anchor=S, padx=100, pady=60) + + def toggle_fullscreen(self, event=None): + self.state = not self.state # Just toggling the boolean + self.tk.attributes("-fullscreen", self.state) + return "break" + + def end_fullscreen(self, event=None): + self.state = False + self.tk.attributes("-fullscreen", False) + return "break" + +if __name__ == '__main__': + w = FullscreenWindow() + w.tk.mainloop()