Monday, March 31, 2025

Wow it has been some time since I updated this...

Not sure why I dug into this old blog to update. But a post every 7 or 8 years seems reasonable.

Things that I have been doing of late:

Home Automation

Main interface Apple HomeKit

I use HomeBridge to get all of my Kasa lights and switches to be available on HomeKit. This seems to work well - I put it on a raspberry Pi. I do notice every few weeks I have to reboot HomeBridge as the devices no longer are reported as responding.

I use Starling to get my google camera and nest protect devices to show up. This works well.

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"

Monday, January 30, 2017

Weekend Project - Raspberry Pi alarm clock

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

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.

Thursday, September 15, 2016

Pi3 as a secure proxy

In a previous post I talked about the cameras I was playing with.

I was looking at a raspberry pi3 that was sitting on my desk and started thinking about the fact that it has both a wifi and an ethernet port - so I decided to see if I could use it as a secure interface to the camera.

Pi3 is on my home wifi.
Pi3 ethernet is directly connected to the ethernet port on a camera.
On the ethernet interface run the following services:

  • ftp server
  • ntp server
  • dhcp server
  • The ethernet network is set up to not forward packets - This lets us take complete control of what the camera can access and what can access the camera.
On the wifi port turn on ssh with key only authentication and an https proxy to the http port on the camera.




Notes on some of the cameras there is a streaming port that is separate from the http port so those will need to be enabled to passthrough as well - perhaps a proxy could be enabled after authentication with a specific ip...

Wednesday, September 14, 2016

Consumer Video Monitoring and Security

I recently had the opportunity to try out different cameras from Foscam, Amcrest and DLink.
What I found is that cameras are marketed using "security", but when it comes to network security they are not very secure.
  • They all use http for setup/admin.
  • They all report motion/audio events via ftp or smtp.
For those not in the network security world - ftp/http/smtp all send data in the clear over your network, you are now relying on the security of your physical or wifi network to protect your data.
One of the vendors has a laughable effort at "Security through obscurity" by making the http port 88 vs 80...
Command to look for open port 80 on a network
  • nmap -p80 192.168.1.0/24 --open
Command to look for 80 and 88 at the same time on a network
  • nmap -p80,88 192.168.1.0/24 --open
Yeah, somebody up to no good will never figure that out...

Friday, September 9, 2016

ASUS Chromebook Flip and Android

Recently I decided to play about with the Android on Chromebook that Google announced.
This seemed like a good candidate for a little computer that could do it all for a low price. It is to bad that this is not charged via USB or it would be the perfect travel computer (I like the idea of only bringing a 2 port USB charger on travel).
I got an ASUS Chromebook Flip - it is an interesting little computer. I like the keyboard, importantly it includes an escape key which is critical for using vi. I like the idea of using Android on the Chromebook but since most of the web based sites have offline modes there are few apps that are really critical to have Android versions of itself. The flip is a bit of a gimmick - they may want to disable the keyboard when it is in flipped mode. BTW as of this writing you need to install the developer release to get the Android mode.

Android apps that I found to be of use on the Chromebook
  • Keypass - Store the passwords you need in a non-online version
  • Termux - Interestingly this revealed that the android is running in kind of a VM on its own internal NATed network so servers that are started on Termux are only accessible by the local Chromebook web browser and can't be hit from a remote computer (this is probably a good thing really).
  • Kindle - The only reason that I ever place it in flip mode
UPDATE:
One of my kid's Chromebooks failed a gravity test so the Flip is now serving as an instant replacement. One of the things I like about Chromebooks is that they are so interchangeable (I just did not anticipate having to use the Flip for that purpose)