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:
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:
dev = InputDevice('/dev/input/event0')
while True:
r,w,x = select([dev], [], [])
for event in dev.read():
To this:
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.