-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMagicMirror.py
338 lines (253 loc) · 10.2 KB
/
MagicMirror.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
"""
Magic/Smart Mirror: A mirror where you can see yourself + some text/images
Coders - Anshul, Prabhat
All ouptput is black & white
Black -> What is to be reflected (area where you see yourself)
White -> What is to be absorbed (area where you see the text/images)
Designed to not require any inputs from user
Only physical connection to the Raspberry pi is the monitor screen
Dimensions of fullscreen are approx 1200 X 700 (x,y)
xyz_module calls other functions, if needed.
Just call the respective xyz_module and you're sorted
after() method format -> window.after(2 * 1000, func)
Here 2 is the number of seconds after which to execute the function again
Terminology: L1 -> 1st label, L2 -> 2nd label, .......
Contains:-
date_module()
time_module()
weather_module()
news_module()
events_module()
gmail_module() this needs email and passwd
"""
from __future__ import print_function #for events
from tkinter import *
import datetime
import urllib.request
import geocoder #latitude,longitude - weather
import requests #news
#for events:-
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
import google.auth.transport.requests
#for gmail:-
import smtplib
import time
import imaplib
import email
#time - Bell MT
def time_module():
"""
Manages all the time related stuff
Outputs time in HH:MM format (final version) & HH:MM:SS format (testing version)
Time error depends on window.after() associated with time_module()
Eg. If process is repeated every 10 secs, error = +- 10 secs which is ok
"""
time = datetime.datetime.now().time()
time = str(time)
pos = time.find(".")
time = time[:pos] #This is the time we want in format HH:MM:SS
time = time[:pos-3]#To do in final display. format HH:MM
L1 = Label(window,text = time, fg = "white", bg = "black",font = "Helvetica 30")
L1.place(x = 1100,y = 50)
window.after(8353, time_module) #updates after 7 secs
def date_module():
"""
Manages all the date related stuff
"""
curr_date = datetime.datetime.now().date()
a = curr_date.strftime("%A") #day of week like thursday, friday
b = curr_date.strftime("%b") #3 letter month like jan,feb
c = curr_date.strftime("%d") #day number in month like 06,23
L1 = Label(window, text = a + ", " + b + " " + c, fg = "white", bg = "black", font = "Helvetica 18")
L1.place(x = 1100,y = 100)
window.after(13751, date_module) #updates after 5 mins
def get_weather():
"""
https://api.darksky.net/forecast/[key]/[latitude],[longitude]
returns summary_useful, temp_useful
temp -> temperature
"""
g = geocoder.ip('me') #An object of that uses the computer's IP address
#Using this means no hardcoding the location
latitude = str(g.latlng[0])
longitude = str(g.latlng[1])
weather_api_token = "98947546ad9ca9997c5d4e9577b84f3d" #I created this & the user doesn't need to change this
URL = "https://api.darksky.net/forecast/" + weather_api_token + "/" + latitude + "," + longitude
req = urllib.request.Request(URL)
webpage = urllib.request.urlopen(req).read()
webpage = str(webpage) #We have our JSON-type webpage in str datatype now
#Operations for summary:-
summary_start = webpage.find("\"summary\":")
summary_end = webpage.find("," , summary_start)
summary_useful = webpage[summary_start+11:summary_end-1]
#operations for temperature:-
temp_start = webpage.find("\"temperature\":")
temp_end = webpage.find("," , temp_start)
fahr_str_temp = webpage[temp_start+14:temp_end]
#below is conversion from fahr_float_temp to celsius_int_temp
#fahr -> fahrenheit
fahr_float_temp = float(fahr_str_temp)
celsius_int_temp = (fahr_float_temp - 32) * (5/9)
celsius_int_temp = int(celsius_int_temp)
temp_useful = str(celsius_int_temp) + "°C"
return summary_useful, temp_useful
def weather_module():
"""Manages all the weather(temperature,summary) related stuff
"""
summary,temp = get_weather()
L1 = Label(window,text = summary, fg = "white", bg = "black",font = "Helvetica 16 bold") #The summary in English
L1.place(x = 1100,y = 150)
L2 = Label(window,text = temp, fg = "white", bg = "black",font = "Helvetica 30 bold") #The temperature in celsius
L2.place(x = 1100,y = 200)
window.after(21139, weather_module) #updates after 2 mins
def news_module():
"""
Displays top 3 news
API taken from BBC
Our API is 04ff9e73215d4a86890ad6a1a2c12ded
"""
main_url = "https://newsapi.org/v2/top-headlines?country=in&apiKey=04ff9e73215d4a86890ad6a1a2c12ded"
# fetching data in json format
open_bbc_page = requests.get(main_url).json()
# getting all articles in a string article
article = open_bbc_page["articles"]
results = [] #This list will contain all the news
for ar in article:
results.append(ar["title"])
results = results[:3] #Only leaving the top 5 news headlines
for i in range(3):
#Here Li actually means L1,L2,L3,L4,L5
#Same label name (variable) can be used to create many labels
#That's why this works
Li = Label(window, text = results[i], fg = "white", bg = "black", wraplength = 450, justify = LEFT, font = "Courier 16")
Li.place(x = 900,y = 400 + i*120)
#Values of y coordinate are 100,150,200,250,300
window.after(24481, news_module) #updates after 5 mins
def events_module():
"""We are using google calender
"""
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
lol = open('token.pickle', 'rb')
#print(lol)
creds = pickle.load(lol)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(google.auth.transport.requests.Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server()
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
#print('Getting the upcoming 10 events')
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=3, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
L0 = Label(window, text = "No upcoming events", fg = "white", bg = "black", font = "Verdana 18 italic")
L0.place(x = 50,y = 50)
ctr = 0
for event in events:
#will break when ctr=3 anyways as we have set maxresults = 3
event_datetime_str = event['start'].get('dateTime', event['start'].get('date'))
description = str(event['summary'])
#sorting datetime related stuff, converting to datetime.timedelta, and outputting as str
pos = event_datetime_str.find("+")
event_datetime_str = event_datetime_str[:pos]
datetime_event_object = datetime.datetime.strptime(event_datetime_str, '%Y-%m-%dT%X')
difference_object = datetime_event_object - datetime.datetime.now()
difference_datetime_str = str(difference_object)
pos_cool = difference_datetime_str.find(":")
difference_datetime_str = difference_datetime_str[:pos_cool] + " hrs later "
#print(difference_datetime_str)
L1 = Label(window, text = description, fg = "white", bg = "black", font = "ErasDemi 18", wraplength = 400)
L2 = Label(window,text = difference_datetime_str,fg = "white", bg = "black", font = "Arial 12", wraplength = 400)
L1.place(x = 50,y = 30 + 80*ctr)
L2.place(x=50,y=65 + 80*ctr)
ctr +=1
window.after(16519, events_module) #updates after 16 secs (testing)
def gmail_module():
FROM_EMAIL = "kapsmirror@gmail.com"
FROM_PWD = "magicmirror"
SMTP_SERVER = "imap.gmail.com"
SMTP_PORT = 993
try:
mail = imaplib.IMAP4_SSL(SMTP_SERVER)
mail.login(FROM_EMAIL,FROM_PWD)
mail.select('inbox')
counter=0
type, data = mail.search(None, 'ALL')
mail_ids = data[0]
id_list = mail_ids.split()
for i in reversed(id_list):
typ, data = mail.fetch(i, '(RFC822)' )
if(counter==3):
break #displays only first 3 mails
for response_part in data:
if isinstance(response_part, tuple):
msg = email.message_from_string(response_part[1].decode('utf-8'))
email_subject = msg['subject']
email_from = msg['from']
email_from = email_from.split('<')[0]
if(email_from[0]=='"'):
email_from=email_from[1:len(email_from)-2]
#print(email_from)
height = 500 + counter*100
L1 = Label(window, text = email_from + ": " + email_subject, fg = "white", bg = "black", wraplength = 220, justify = LEFT, font = "Times 16")
L1.place(x = 50,y = height)
counter = counter+1
except Exception as e:
print(str(e))
window.after(9829, gmail_module) #updates after 9 secs (testing)
"""
Landscape -> 1300 x 700
Vertical -> 700 x 1300
how to set font,fontsize -> label.config(font=("Courier", 44))
"""
window = Tk() #Creating the object i.e. the window
window.configure(background='black')
window.attributes("-fullscreen", True)
date_module()
time_module()
weather_module()
news_module()
events_module()
gmail_module()
def exitprogram(): #comment this out in final product
"""
Comment out this function and below few lines to use without exit button
If you do so, to exit you should do:-
Ctrl + Alt + t (open terminal)
google-chrome
"""
window.destroy()
exit()
#We use this button as we are in fullscreen and there's no way for us to exit
B1 = Button(window, text = "Exit", fg="red", command = exitprogram)
B1.place(x = 600, y = 500)
#The following functions occur infinitely after the given time period
window.after(8353, time_module) #updates after 8 secs max
window.after(13751, date_module) #updates after 13 secs (testing)
window.after(21139, weather_module) #updates after 21 secs (testing)
window.after(24481, news_module) #updates after 24 secs (testing)
window.after(16519, events_module) #updates after 16 secs (testing)
window.after(9829, gmail_module) #updates after 9 secs (testing)
window.mainloop()