1
+ const fs = require ( 'fs' )
2
+ const readline = require ( 'readline' )
3
+ const { google} = require ( 'googleapis' )
4
+ const https = require ( 'https' )
5
+ const express = require ( 'express' )
6
+ var app = express ( )
7
+
8
+ const LAMPAPICODE = 'INSERT API CODE HERE'
9
+ const STARTOFWORK = 8
10
+ const ENDOFWORK = 18
11
+ const REACTIONHOURS = 3
12
+ const SCOPES = [ 'https://www.googleapis.com/auth/calendar.readonly' ]
13
+ const TOKEN_PATH = 'token.json'
14
+ let calendarObjects = { }
15
+ let eventList = { }
16
+ let stressLevel = 0
17
+ let timeToNextEvent = - 1
18
+ let standbyTime = 0
19
+ let eyeBrightness = 120
20
+ let lampState = 0
21
+ let brightnessLevel = 0.5
22
+ let oldBrightnessLevel = 0.5
23
+
24
+ fs . readFile ( 'credentials.json' , ( err , content ) => {
25
+ if ( err ) return console . log ( 'Error loading client secret file:' , err )
26
+
27
+ let userId = "user_01"
28
+ authorize ( JSON . parse ( content ) , userId , getCalendarObject )
29
+ } )
30
+
31
+ function authorize ( credentials , userId , callback ) {
32
+ const { client_secret, client_id, redirect_uris} = credentials . installed
33
+ const oAuth2Client = new google . auth . OAuth2 (
34
+ client_id , client_secret , redirect_uris [ 0 ] )
35
+
36
+ // Check if we have previously stored a token.
37
+ fs . readFile ( userId + "-" + TOKEN_PATH , ( err , token ) => {
38
+ if ( err ) return getAccessToken ( oAuth2Client , userId , callback )
39
+ oAuth2Client . setCredentials ( JSON . parse ( token ) )
40
+ callback ( oAuth2Client , userId )
41
+ } )
42
+ }
43
+
44
+ function getAccessToken ( oAuth2Client , userId , callback ) {
45
+ const authUrl = oAuth2Client . generateAuthUrl ( {
46
+ access_type : 'offline' ,
47
+ scope : SCOPES ,
48
+ } )
49
+ console . log ( 'Authorize this app by visiting this url:' , authUrl )
50
+ const rl = readline . createInterface ( {
51
+ input : process . stdin ,
52
+ output : process . stdout ,
53
+ } )
54
+ rl . question ( 'Enter the code from that page here: ' , ( code ) => {
55
+ rl . close ( ) ;
56
+ oAuth2Client . getToken ( code , ( err , token ) => {
57
+ if ( err ) return console . error ( 'Error retrieving access token' , err )
58
+ oAuth2Client . setCredentials ( token )
59
+ // Store the token to disk for later program executions
60
+ fs . writeFile ( userId + "-" + TOKEN_PATH , JSON . stringify ( token ) , ( err ) => {
61
+ if ( err ) return console . error ( err )
62
+ console . log ( 'Token stored to' , userId + "-" + TOKEN_PATH )
63
+ } )
64
+ callback ( oAuth2Client , userId )
65
+ } )
66
+ } )
67
+ }
68
+
69
+ function getCalendarObject ( auth , userId ) {
70
+ calendarObjects [ userId ] = google . calendar ( { version : 'v3' , auth} )
71
+ startScript ( userId )
72
+ }
73
+
74
+ async function getEventData ( userId , email ) {
75
+ let today = new Date ( )
76
+ let endTime = new Date ( )
77
+ endTime . setHours ( endTime . getHours ( ) + REACTIONHOURS , 0 , 0 , 0 )
78
+ let upcomingEventsOfUser = [ ]
79
+
80
+ let err , res = await calendarObjects [ userId ] . events . list ( {
81
+ calendarId : 'primary' ,
82
+ timeMin : today . toISOString ( ) ,
83
+ timeMax : endTime . toISOString ( ) ,
84
+ maxResults : 100 ,
85
+ singleEvents : true ,
86
+ orderBy : 'startTime' ,
87
+ } )
88
+ if ( err ) return console . log ( 'The API returned an error: ' + err )
89
+ const events = res . data . items
90
+ if ( events . length ) {
91
+ events . map ( ( event , i ) => {
92
+ const start = event . start . dateTime || event . start . date
93
+ const end = event . end . dateTime || event . end . date
94
+ let dateStampEventStart = new Date ( start )
95
+ let dateStampEventEnd = new Date ( end )
96
+ let eventDuration = Math . floor ( ( dateStampEventEnd . getTime ( ) - dateStampEventStart . getTime ( ) ) / 1000 / 60 )
97
+ let accepted = 'no_choice_made'
98
+
99
+ if ( event . attendees && event . attendees . length > 0 ) {
100
+ event . attendees . forEach ( attendee => {
101
+ if ( attendee . email == email || attendee . self ) {
102
+ accepted = attendee . responseStatus
103
+ }
104
+ } )
105
+ }
106
+
107
+ upcomingEventsOfUser . push ( {
108
+ title : event . summary ,
109
+ start : dateStampEventStart ,
110
+ end : dateStampEventEnd ,
111
+ duration : eventDuration ,
112
+ attendees : event . attendees ? event . attendees . length : 0 ,
113
+ isOnline : event . conferenceData ? true : false ,
114
+ isOrganizer : ( event . organizer && event . organizer . email && event . organizer . email == email ) || ( event . organizer && event . organizer . self ) ? true : false ,
115
+ accepted : accepted ,
116
+ } )
117
+ } )
118
+ } else {
119
+ console . log ( 'No upcoming events found.' )
120
+ }
121
+
122
+ eventList [ userId ] = upcomingEventsOfUser
123
+ }
124
+
125
+ function calculateStressLevel ( userId ) {
126
+ let totalDuration = 0
127
+ let tmpTime = new Date ( )
128
+ let endOfReactionTimestamp = new Date ( )
129
+ endOfReactionTimestamp . setHours ( endOfReactionTimestamp . getHours ( ) + REACTIONHOURS )
130
+ let foundReminder = false
131
+ let tmpTimeToNextEvent = - 1
132
+ eventList [ userId ] . forEach ( event => {
133
+ if ( tmpTimeToNextEvent < 0 ) {
134
+ tmpTimeToNextEvent = Math . floor ( ( event . start . getTime ( ) - tmpTime . getTime ( ) ) / 1000 / 60 )
135
+ }
136
+ if ( event . end . getTime ( ) <= endOfReactionTimestamp . getTime ( ) && event . start . getTime ( ) >= tmpTime . getTime ( ) ) {
137
+ // event is completely within the reaction time -> count entire event
138
+ //duration in min
139
+ console . log ( "full event found " + event . title )
140
+ totalDuration += event . duration
141
+ }
142
+ else if ( event . end . getTime ( ) > endOfReactionTimestamp . getTime ( ) ) {
143
+ console . log ( "partial event found in reactiontime " + event . title )
144
+ totalDuration += Math . floor ( ( endOfReactionTimestamp . getTime ( ) - event . start . getTime ( ) ) / 1000 / 60 )
145
+ }
146
+ else {
147
+ console . log ( "already ongoing event found " + event . title )
148
+ totalDuration += Math . floor ( ( event . end . getTime ( ) - tmpTime . getTime ( ) ) / 1000 / 60 )
149
+ }
150
+
151
+ let differenceStarting = ( event . start . getTime ( ) - tmpTime . getTime ( ) ) / 1000
152
+ console . log ( "test starting " + differenceStarting )
153
+ if ( differenceStarting >= 450 && differenceStarting <= 600 ) {
154
+ console . log ( "Found event reminder" )
155
+ foundReminder = true
156
+ }
157
+ } )
158
+
159
+ if ( tmpTimeToNextEvent < 0 ) {
160
+ tmpTimeToNextEvent = - 1
161
+ }
162
+ let date = new Date ( )
163
+ let secondsUntilEndOfWorkDay = REACTIONHOURS * 60 * 60
164
+ if ( date . getHours ( ) >= ENDOFWORK ) {
165
+ return [ - 1 , tmpTimeToNextEvent ]
166
+ }
167
+ if ( date . getHours ( ) < STARTOFWORK ) {
168
+ return [ - 1 , tmpTimeToNextEvent ]
169
+ }
170
+ return [ Math . floor ( totalDuration * 60 / secondsUntilEndOfWorkDay * 100 ) > 0 ? Math . floor ( totalDuration * 60 / secondsUntilEndOfWorkDay * 100 ) : 0 , tmpTimeToNextEvent ]
171
+ }
172
+
173
+ async function startScript ( userId ) {
174
+ let email = await getUserEmail ( userId )
175
+
176
+ setInterval ( async function ( ) {
177
+ await getEventData ( userId , email )
178
+ let newValuesCalendar = calculateStressLevel ( userId )
179
+
180
+ stressLevel = newValuesCalendar [ 0 ]
181
+ timeToNextEvent = newValuesCalendar [ 1 ]
182
+ if ( stressLevel < 0 ) {
183
+ standbyTime = 1
184
+ }
185
+ else {
186
+ standbyTime = 0
187
+ }
188
+
189
+ console . log ( "Current stress level: " + stressLevel )
190
+ console . log ( "Time to next event: " + timeToNextEvent )
191
+ console . log ( "Outside working hours: " + standbyTime )
192
+
193
+ if ( standbyTime ) {
194
+ lampState = - 1
195
+ setLightbulbOff ( )
196
+ }
197
+ else if ( timeToNextEvent <= 5 && timeToNextEvent >= 0 ) {
198
+ // NOTIFICATION (250 = BLUE)
199
+ lampState = 250
200
+ setLightbulbHSL ( lampState , 1 , brightnessLevel , 2.0 )
201
+ }
202
+ else {
203
+ if ( stressLevel > 100 ) {
204
+ stressLevel = 100
205
+ }
206
+ // 100 = GREEN; 0 = RED
207
+ lampState = 100 - stressLevel
208
+ setLightbulbHSL ( lampState , 1 , brightnessLevel , 2.0 )
209
+ }
210
+ oldBrightnessLevel = brightnessLevel
211
+ } , 30000 )
212
+ }
213
+
214
+ async function getUserEmail ( userId ) {
215
+ let err , res = await calendarObjects [ userId ] . calendarList . list ( { } )
216
+ if ( err ) return console . log ( 'The API returned an error: ' + err )
217
+ const cal = res . data . items
218
+ if ( cal . length ) {
219
+ console . log ( "user:" + userId + " // email:" + cal [ 0 ] . id )
220
+ return cal [ 0 ] . id
221
+ }
222
+ return null
223
+ }
224
+
225
+ function setLightbulbHSL ( h , s , l , duration ) {
226
+ const data = JSON . stringify ( {
227
+ power :"on" ,
228
+ color :`hue:${ h } saturation:${ s } brightness:${ l } ` ,
229
+ duration : duration ,
230
+ } )
231
+
232
+ const options = {
233
+ hostname : 'api.lifx.com' ,
234
+ port : 443 ,
235
+ path : '/v1/lights/all/state' ,
236
+ method : 'PUT' ,
237
+ headers : {
238
+ 'Content-Type' : 'application/json' ,
239
+ 'Content-Length' : data . length ,
240
+ 'Authorization' : 'Bearer ' + LAMPAPICODE ,
241
+ }
242
+ }
243
+ const req = https . request ( options , res => {
244
+ console . log ( `statusCode: ${ res . statusCode } ` )
245
+ } )
246
+
247
+ req . on ( 'error' , error => {
248
+ console . error ( error )
249
+ } )
250
+
251
+ req . write ( data )
252
+ req . end ( )
253
+ }
254
+
255
+ function setLightbulbOff ( ) {
256
+ const data = JSON . stringify ( {
257
+ power :"off" ,
258
+ duration : 2.0 ,
259
+ } )
260
+
261
+ const options = {
262
+ hostname : 'api.lifx.com' ,
263
+ port : 443 ,
264
+ path : '/v1/lights/all/state' ,
265
+ method : 'PUT' ,
266
+ headers : {
267
+ 'Content-Type' : 'application/json' ,
268
+ 'Content-Length' : data . length ,
269
+ 'Authorization' : 'Bearer ' + LAMPAPICODE ,
270
+ }
271
+ }
272
+
273
+ const req = https . request ( options , res => {
274
+ console . log ( `statusCode: ${ res . statusCode } ` )
275
+ } )
276
+
277
+ req . on ( 'error' , error => {
278
+ console . error ( error )
279
+ } )
280
+
281
+ req . write ( data )
282
+ req . end ( )
283
+ }
284
+
285
+ app . get ( '/mattis' , function ( req , res ) {
286
+ res . end ( eyeBrightness + ";" + stressLevel + ";" + timeToNextEvent + ";" + standbyTime )
287
+ } )
288
+
289
+ app . get ( '/smart-cup' , function ( req , res ) {
290
+ var scale = req . query . scale
291
+ var temperature = req . query . temperature
292
+
293
+ var scaleNumber = 0.0
294
+
295
+ if ( Number . isNaN ( Number . parseFloat ( scale ) ) ) {
296
+ scaleNumber = 0.0
297
+ }
298
+ else {
299
+ scaleNumber = ( - 1 * Number . parseFloat ( scale ) ) - 84
300
+ }
301
+
302
+ if ( scaleNumber > 30 ) {
303
+ //empty standard compatible cup should be around 50 (most cups are a bit lighter, therefore -40)
304
+ let tmpBrightness = ( scaleNumber - 40 ) / 50
305
+
306
+ if ( tmpBrightness < 0.0 ) {
307
+ tmpBrightness = 0.0
308
+ }
309
+ if ( tmpBrightness > 0.8 ) {
310
+ tmpBrightness = 0.8
311
+ }
312
+ brightnessLevel = ( 1.0 - tmpBrightness ) * 0.6
313
+ console . log ( "New brightness level: " + brightnessLevel )
314
+ if ( Math . abs ( brightnessLevel - oldBrightnessLevel ) > 0.1 ) {
315
+ if ( lampState < 0 ) {
316
+ setLightbulbOff ( )
317
+ }
318
+ else {
319
+ setLightbulbHSL ( lampState , 1 , brightnessLevel , 2.0 )
320
+ }
321
+ oldBrightnessLevel = brightnessLevel
322
+ }
323
+ }
324
+ } )
325
+
326
+ var server = app . listen ( 9085 , function ( ) {
327
+ var host = server . address ( ) . address
328
+ var port = server . address ( ) . port
329
+ console . log ( "Example app listening at http://%s:%s" , host , port )
330
+ } )
331
+
0 commit comments