Monday, February 13, 2017

has a/is a/multiple inheritance/interface classes

I want the main class for my app to be basically one big event handler ie
class MonkeyMouse
  def onMouseUp(self)
    ...
  def onWheel(self, direction)
    ...
  def onCalendarUpdate(self)
    ...
  def onAlarm(self)
    ...
Right now I am doing that by doing multiple inheritance (using the "is a" solution)
class MonkeyMouse(timerAlarm.TimerAlarm, mouseEvent.MouseEvent, google_cal.GoogleCal, pianobar.PianoBar)
  def onMouseUp(self)
    ...
  def onWheel(self, direction)
    ...
  def onCalendarUpdate(self)
    ...
  def onAlarm(self)
    ...
The problem comes up when I create a method in 2 classes with the same name ie both the mouseevent and the timerevent classes both spawn threads calling a method that I initially called "worker". Obviously the name conflict caused issues.  Since it is me writing all the code I can obviously get around this issue, but also since it is me I have a high likelihood of causing conflicts for methods that do the same thing.

Compounding the issue is the desire to not spend huge amounts of time creating the perfect solution for a fun little app...

Another way to get around this would be to have the mouseevent class take a mouseevent handler class and have my main app inherit from each handler class, this would mean that the onXXX methods are the only place that I need to avoid conflict. This is kind of a pain in that I need to put try: / except around every call to make sure the method is defined.

The third way (using a "has a" solution) involves registering a callback method for each item that we are interested in - this still involves putting try/except around calls like self.mouseUpCallback() - however if we want to be lazy (cough) we can set all of our callbacks to be to null functions so then we just have to register for the callbacks that we care about and we can assume that the calls will work ie
class MouseEvent
   def __init__(self):
      super(MouseEvent, self).__init__()
      self.mouseUpCallback = self.nullFunc 
  def worker(self):
      ...
      self.mouseUpCallback()
      ....
  def nullFunc(self)
    pass 
Then all we need to do is write bind methods - I like this as it is the most elegant.

The only issue then is that we are not on a single thread that handles events "onMouseUp" could be called concurrently with "onAlarm" meaning they could have resource contention so we would need to put thread protection into our code. If we want to mitigate this (and avoid it in the onXxx code) we could instead create a event handler class and have the main thread run it in a loop. The other threads (mouse, calendar, alarm etc) would register their event and then signal the handler thread to wake it up using a condition. This would mean that the event handler class would be the only place that had to have mutex/thread awareness and the onXxx would be run synchronously avoiding many asynchronous headaches. I may have talked myself into doing it that way even though it is just me writing this app as it will let the app be more reusable...

Friday, February 10, 2017

Notes on pi alarm clock

I have been busy at work and have not done much with the project the past week or 2.

So far I have just created 3 classes to make the proof of concept code into reusable modules.
mouseevent.py, pianobar.py, google_cal.py

The mouseevent class starts a thread inside it and thus creates an event loop to trigger methods like onMouseUp when you inherit from mouseevent.

The pianobar class takes the work from http://www.instructables.com/id/Pandoras-Box-An-Internet-Radio-player-made-with and turns it into a class with methods like start, volUp, volDown, pause, etc

The google_cal.py allows you to override a method called handleEvent(calname, event) which is passed each event as the weeks calendar is iterated over from google. This class may turn into a data structure that calles an onChange method.

The code is being hosted in a private repository on github right now, I suppose I could make it public if there is ever any interest.

Some random notes on installing what I am calling monkey clock (not sure about my apparent fascination with monkeys, but...)
From a fresh Raspbian system these are the commands to get the system setup to play audio and read mouse events.

First we will do the typical refresh of install.
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
Now lets install the packages that I seem to auto install (I probably dont need these but...)
  • vim for editing files in a civilized manner
  • build-essential g++
  • autoconf - autoconf for configure files

sudo apt-get install vim build-essential autoconf
These packages are needed for various things for an alarm clock
  • ntpdate to keep the date and time correct
  • pianobar to play pandora
  • python-dev you will need to install evdev
  • mplayer to play mp3 files.
sudo apt-get install python-dev ntpdate pianobar mplayer
Install google api and evdev (for mouse events)
sudo pip install --upgrade google-api-python-client
sudo pip install evdev
If you are plugged in to an hdmi port and want to hear output from speakers you will need to tell it to send the output that way
#if you have your hdmi setup as the output for audio set it to the headphone jack
amixer cset numid=3 1
You are going to want to have the device be headless so lets enable ssh so that we can log in remotely.
https://www.raspberrypi.org/documentation/remote-access/ssh/
Note that we are going to want to disable password login in /etc/ssh/sshd_config and create keys to login https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2

In the stock /etc/ssh/sshd_config you want to set the following line to turn off ssh passwords "PasswordAuthentication no"