Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Wednesday, October 07, 2015

Raspberry Pi Finally Conquers Userland

Raspberry Pi developers have had quite a coup on their hands this past few weeks. The "official" Raspberry Pi Linux distribution Raspian was just upgraded to Debian 8, or "Jessie." This provides a huge number of wins - the 4.1 release of the Linux kernel, latest glibc and build chain updates, more native packages (like Node.JS and wiringPi), and device trees. Oh, sweet device trees.

While the current Raspian distribution still relies on wiringPi 2.24, the most recent 2.29 version has a much nicer way of addressing GPIO in userspace by exposing the GPIO ports in /dev/gpiomem. All too often Raspberry Pi developers run GPIO apps as root to access the array of general purpose I/O pins, however this leads to all the lovely security holes and vulnerabilities that privileged access brings. You never want Apache or Python or any user-created apps running as root - so instead you must find a way to export these ports and allow unprivileged users to access them. Traditionally this has been done using wiringPi's export utility, however the latest gpiomem exposure seems to be much cleaner.

With Jessie I've been able to significantly cut the complexity of installing Garage Security and Sprinkler Switch. I don't need to manually install wiringPi, Node.JS, Video4Linux and a number of other packages. Things seem to largely "just work" as one might expect of a modern distro. One example is that Motion has been updated and appears to be pre-packaged on Raspian, and the necessary Video4Linux bcm2835-v4l2 kernel module properly creates a /dev/video0 device. CPU utilization appears to be much lower with the current stack, and it appears that I can just tweak Motion's configs to save videos in an HTML 5-friendly way rather than transcoding them with a script.

Garage Security and Sprinker Switch are being updated now for Jessie and testing is underway... the new Jessie builds are looking very promising so far.

Friday, September 25, 2015

XMPP (Jabber) as a Message Broker

For a long while I’ve relied on Jabber/XMPP support within Google Talk to communicate with back-end systems like my Garage Security monitor. Garage Security could push notifications to me when motion was detected, and I could reply back to ask for camera snapshots or current temperatures. It’s almost as if I was using XMPP and Google’s Talk servers as a message bus; everything was a request/response pair that I could receive as notifications in a nice lil’ mobile interface. This was a superior approach to having a peer-to-peer communication channel over the Internet at large - I could keep my firewall completely closed and instead publish events to a trusted broker over at Google HQ. I essentially treated Google Talk like a hosted RabbitMQ instance.

This "XMPP broker” approach continued to work after Google moved from Google Talk to Hangouts and dropped full XMPP support (notably for federation), however things appear to have become a bit more difficult when two different systems (like Garage Security and my new Sprinkler Switch) want to share the same Hangouts user ID. Previously both systems would receive an inbound message, so I would filter by a token in the message body. If I asked for “garage status,” Garage Security would catch the “garage” keyword and respond while Sprinkler Switch would just ignore it. As Hangouts has turned the XMPP support decidedly more text-message-ish, it seems now the last system to authenticate will starve out the previous system, and only one system will actually receive the messages.

This is not outside of the XMPP spec it seems, and the protocol itself specifies two ways for the systems themselves to deal with the issue:

  1. When connecting to the XMPP server set the priority for your connection. A higher priority is more likely to get inbound messages.
  2. Specify a resource name within your XMPP user identifier. This allows a system to be uniquely addressable with the same username.

The first option doesn’t necessarily help my situation - I want both systems to receive inbound messages. The second option is possible using XMPP’s definition of user IDs… where a user identifier is actually the composite of:

  • The username that is used for authentication
  • The domain that the user resides within
  • The resource that uniquely identifies who is signing in
Using this schema, I could provide SleekXMPP a JabberID (its native user identifier) of chuckleface@gmail.com/garage and have it uniquely identify Garage Security, while chuckleface@gmail.com/sprinkler uniquely identifies Sprinkler Switch. It’s not entirely unlike the routing key in AMQP or a topic name in JMS… chuckleface could be considered your message type, gmail.com could be considered your exchange, and garage could be considered your ID. Or something like that. It makes sense in my head at least.

Hangouts, however, just cares about chat messages. It could give two craps about my resource name. There’s no way to specify that in a contact either… with Hangouts you specify an e-mail address which in turn becomes a username and a domain. That’s fine for chat… but when I want to address an individual system I’m kinda outta luck. Hangouts will just reply back to the last resource that sent it a message - no way to specify a specific resource.

I've posted a demo using Python on GitHub which lets you build a quick XMPP client. An example might be:

>>> from jabtest import Jabber
>>> jab1 = Jabber('test@gmail.com', 'apikeyh4x0r5', 'testone')
Opened XMPP Connection
>>> jab2 = Jabber('test@gmail.com', 'apikeyh4x0r5', 'testone')
Opened XMPP Connection
>>> jab1.send_msg('deckerego@gmail.com', 'Testing One')
Sending message: Testing One

I don't have a fantastic solution for now... so in the interim I've disabled Jabber support for Sprinkler Switch.

Wednesday, October 30, 2013

Your Barn Door is On Display

After I buttressed the Pi as best as I could, I constructed a simple webapp to allow users to view the security cam feed and activate the button on the garage remote. It was surprisingly straight-forward to render an MJPEG feed on an HTML5 canvas, but it took a bit more doings to expose GPIO as a minimal REST call.

Before I could deploy the webapp a few additional packages needed to be deployed for Python to access everything:
  1. Installed python-distribute so we can use Python's easy_install
  2. Installed pip using easy_install (how meta) so we can easily install application dependencies
  3. Installed libapache2-mod-wsgi to permit Apache to act as a Python application server
  4. Cloned the GarageSecurity repository, which includes the Bottle webapp and some admin configs/scripts
  5. Installed GarageSecurity's dependencies using pip install -r pip_requirements.txt
  6. Allowed www-data to access the GPIO port using the WiringPi utility

One big condition I held was that the webapp should not be granted root access, even if it was indirect access with a setuid script. The WiringPi utilities allow one to create GPIO devices handles that can be accessed by an unprivileged user such as www-data. By adding an entry within /etc/rc.local for the WiringPi utility the devices will be created on boot for use by a user within the gpio group. The WiringPi Python libraries then use these devices to control the GPIO pins. This took a few hours of experimentation, and a huge amount of thanks go to Sebastian Ă–sterlund's WiringPi post for helping me figure this out.

One could use a REST framework such as WebIOPi to expose GPIO access over a REST interface, but it looked like this implementation needed privilaged access and the webapp didn't require 99% of the features that WebIOPi ships with. Instead, I leveraged Bottle to expose the WiringPi library as a REST call, which permits a client-side application to issue a remote call and active the garage door remote.

The MJPEG stream provided by Motion for the camera was only bound to the localhost interface, however I proxied it through Apache for external exposure. Multipart MJPEG streams are not directly supported by many browsers anymore; instead it is fairly straightforward exercise to have JavaScript functions fetch the stream and then paint the images directly onto an HTML5 canvas. This surprisingly just took eight lines of JavaScript to accomplish; it took me more time to figure out how to scale the viewport and a button for mobile devices than it took to render the webcam feed.

I know I'm definitely not early to the garage door hacking scene - there are several other projects using Arduino with mobile front-ends, some adapted to use the Raspberry Pi with relays, others with wireless interfaces and mobile webapps. I'm a bit partial to this approach because it has a fairly low part count (one resistor, one MOSFET, some wire, a mini breadboard, one universal garage door remote, a cheap webcam and a Raspberry Pi), it doesn't use relays and the web application does not require privilaged access to low-level resources.

Feel free to check out the evolving webapp - it is now managed under GitHub at http://github.com/deckerego/GarageSecurity/. I will continue tweaking it a bit and eventually fitting it with some sort of user interface, but I also want to move along and use Motion's External Commands to e-mail me whenever it detects motion. Unfortunately HP's HD-3110 has an auto-focus that keeps kicking on and registering as a motion event, so I might dig deeper to see how to disable the feature. Of course I still need an enclosure as well... right now bare wire and board are just sitting out on a shelf. One stray squirt gun and all is lost.