Background:
I have a cool app on my phone called Alarm Calendar Plus that I use as my alarm clock as well as a reminder for the kids to head out to the bus.It has a few issues - no regular expression matching and no way to do boolean expressions ((A and not B) or C). Right now I have three rules to wake me at 7 if any of my kids have school that day, one of those rules wins every morning and the others are reported as "missed" and I have to dismiss them manually. I also have 3 rules relating to going out to the bus 2 of them are duplicates. I had an alarm for "art" that was picking up "party" as a match. When I am on travel I have to manually disable all 6 of the rules so that I dont have my alarm going off at odd times.
We have a no devices in the bedroom rule for my kids right now. They also have been known to forget to turn alarms off on weekends. I wanted them to be able to listen to music and also have alarms only on days that they needed alarms on.
I decided that I could solve many of these requirements with a raspberry pi hooked to speakers.
Requirements:
- It needs to use google calendar to decide if it should set the alarm or not - the entire family is set up using individual calendars that are all shared with each other.
- It needs to be easy to deal with.
- It would be cool if it would play music from online streaming. Most of the family has a Pandora account but it would be nifty to also do google play/amazon streaming/etc.
Gather the functionality:
Scripting language:
I decided to use python because it has become popular and I normally use perl or javascript so forcing myself to use a new scripting language to implement something I wanted is really the only way to learn a new language.BTW I dont like indent based languages - my brain has been trained for curly braces....
Pandora:
First lets get pandora working:I found this link that looked promising:
http://www.instructables.com/id/Pandoras-Box-An-Internet-Radio-player-made-with
This ends up using pianobar for the Pandora interface.
https://6xq.net/pianobar/
The use of a fifo to have an external program send commands is the part that I will pull from the instructable.
To run pianobar in the background do this:
$ nohup pianobar < /dev/null &
Interface:
The instructable had buttons that use the pi's gpio - I was looking for big buttons that could be used to turn an alarm on and off and pricing them out when I realized that I could just attach a mouse to the thing and use the buttons on the mouse (I know that is not as cool as hardwired buttons but it meant that I could quickly get something working).
Basic mouse:
https://smile.amazon.com/gp/product/B005EJH6RW/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1
More buttons:
https://smile.amazon.com/gp/product/B01BC4TXXC/ref=crt_ewc_title_dp_1?ie=UTF8&psc=1
While I was looking at those I also saw this numeric keypad which would give more options (perhaps to many?)
https://smile.amazon.com/gp/product/B01E8TTWZ2/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1
Basic mouse:
https://smile.amazon.com/gp/product/B005EJH6RW/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1
More buttons:
https://smile.amazon.com/gp/product/B01BC4TXXC/ref=crt_ewc_title_dp_1?ie=UTF8&psc=1
While I was looking at those I also saw this numeric keypad which would give more options (perhaps to many?)
https://smile.amazon.com/gp/product/B01E8TTWZ2/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1
Reading USB events:
After some googling I found evdev to read the events from the mouse:https://python-evdev.readthedocs.io/en/latest/
and some sample code here:
https://www.raspberrypi.org/forums/viewtopic.php?p=339731
BTW: To make it read from all the input devices so that I did not have to figure out which one was the mouse I modified the example code to create an array of all the /dev/input/event[X] devices.
I changed:
To this:dev = InputDevice('/dev/input/event0') while True: r,w,x = select([dev], [], []) for event in dev.read():
files = [] i=0 while os.path.exists('/dev/input/event' + str(i)): files.append(InputDevice('/dev/input/event' + str(i))) i += 1 while True: r,w,x = select(files, [], []) for dev in r: for event in dev.read():
I then did a quick test writing commands to a fifo that was being read by pianobar (text based Pandora app)
I need to think on the most intuitive interface - right now I have right mouse pause/play and scroll wheel volume.
Reading google calendar:
Google makes it easy to get a hello world type app up:https://developers.google.com/google-apps/calendar/quickstart/python
After a successful test I modified the code to only show the next week and to show the data from all of the calendars that I had.
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time next = datetime.datetime.utcnow() + datetime.timedelta(weeks=1) nextstr = next.isoformat() + 'Z' page_token = None while True: calendar_list = service.calendarList().list(pageToken=page_token).execute() for calendar_list_entry in calendar_list['items']: eventsResult = service.events().list( calendarId=calendar_list_entry['id'], timeMin=now, maxResults=10, singleEvents=True, timeMax=nextstr, orderBy='startTime').execute() events = eventsResult.get('items', []) if not events: print('No upcoming events found for ' + calendar_list_entry['summary']) for event in events: start = event['start'].get('dateTime', event['start'].get('date')) end = event['end'].get('dateTime', event['end'].get('date')) print(calendar_list_entry['summary'], start, end, event['summary']) page_token = calendar_list.get('nextPageToken') if not page_token: break
Storing user preferences and data:
Feeling confident after reading data from the calendar I decided that user prefs and data could be stored on google as well.https://developers.google.com/sheets/api/quickstart/python
It turns out that Google Docs is not as mature as Google Sheets for api's so I will have to figure out a schema that uses a spreadsheet (I was going to just store a JSON string in a doc).
I modified their quickstart after getting it to work to show each row as a comma delimited string
rangeName = 'Sheet1!A1:E' result = service.spreadsheets().values().get( spreadsheetId=spreadsheetId, range=rangeName).execute() values = result.get('values', []) if not values: print('No data found.') else: for row in values: print(', '.join(row))
Text to speech:
I found this site and used the google method to implement a quick text to speech interface:http://elinux.org/RPi_Text_to_Speech_(Speech_Synthesis)
Alarm sounds:
Grabbing the mplayer command from the google text to speech I was able to get the command to play an alarm as an mp3
/usr/bin/mplayer --really-quiet -ao alsa -noconsolecontrols alarm.mp3
Find your favorite mp3 to use as an alarm. I found some cool ones here
Conclusion:
OK, I did not finish the implementation, but I think I have all the unknowns figured out.
If/when I have it all working I can post the full implementation.