Archive for the 'hobbies' Category

Weekend Rails Hacking

Monday, May 5th, 2008

For the past 4 years I’ve been using evite to manage the RSVPs for our memorial day weekend party. Given that it’s a pretty large scale pot luck event, it’s helpful to have a system where people can respond with a message that others can see. The reduces my need to field “what should we bring” questions, as you can easily see what everyone else is bringing and react accordingly.

Evite sucks. While it doesn’t force attendees to make accounts, it makes it look like it does. The evite.com emails tend to catch as spam. And the interface is now dubious under firefox. The idea is still good, but it hasn’t really ramped with the trends in the rest of the service web application space.

One of the key things I wanted in an evite replacement is getting rid of user logins. Given an event, and an email address, you can come up with a unique key that qualifies that person for that event. That means the user just follows a link, and they are in. Links are unique for people. If you make the key a hash of the person’s email and some secret seed key for the event, you’ve got something cryptographically strong as well. No one can modify another person’s entry because the key needs to match before you get any info.

Saturday was a rainy day, so I built this system. By Saturday night I had most of it working, and had rolled this out live by Sunday afternoon. This was my first rails 2.0 app, so I needed to catch up on a few things along the way. Things I learned:

  • Rails 2 creates scaffolds in a slightly new way. That threw me for a bit, as I had already built models for most of my objects before creating scaffolds. The new way (putting attributes on the command line) looks like it is designed to make rails tooling easier.
  • ActionMailer is crazy easy. It even does multipart mixed emails really easily. My mhvlug mailer script for month announcements is going to need to be converted to this at some point.
  • Rails has a word_wrap function in the view context. Of course it does, why did I even doubt that.
  • The google maps API is impressive. I had maps based on event location within 60 minutes of signing up for my Google Maps API key.
  • The f= param on maps.google.com is which fuction to drive. q: location query, d: directions. That took a little bit of reading urls to realize.
  • It’s pretty easy to integrate mercurial push to auto restart a rails app if it’s running under passenger.
  • If you are running multiple versions of rails applicatoins under passenger, delete all the rails links in vendor/ so that it picks up the right rails environment.
  • arround_filter in rails is really handy to catch generic exceptions and dump people off to an error page that isn’t the default rails one.

All in all, I was really happy how this turned out. As soon as I get some free time I’ll genericize the bits of the app that I coded just for our event, and get this out on rubyforge. I only wish there was a rails equiv of gems, as I’ve still found that it isn’t entirely clear how to best package a rails application as an easy to download open source component.

Popularity: 13% [?]

Dague.net move

Wednesday, April 30th, 2008

Once upon a time I said I would never host my own email (or email in general), as it was a pain I didn’t want. Then, I ended up hosting email for mhvlug.org because it turned out to be the simplest solution. A week later I installed postgrey, and watched the spam rates drop by 80%. And it was good.

A couple things changed in the last year. Linode went from UML to Xen, which definitely makes each linode more powerful. My shared hosting company stopped being helpful. I had a couple of small outages. They had moved from a knowledgable support staff, to a support pool that was clueless, and never seemed to understand the ways in which their system was broken. And, after hosting mhvlug email for a while with no issues, it seemed reasonable that dague.net email would be safe there as well.

Backups (thanks to backuppc) have been ramped up from every 24 hrs to every 6 hrs on the box, to narrow my window in which I can screw things up. Only one set of email delays so far, mostly because I set a wrong postfix param over the weekend, which may have been blocking mhvlug.org email as well. But that is resolved now. Dan will at least thing I’m a real man now. ;)

Popularity: 22% [?]

Mono 1.9 install script

Saturday, March 29th, 2008

Unfortunately no one has made ubuntu packages yet, however here is a script that I built based on Dirk’s post to automate mono 1.9 installation onto Ubuntu environments.

#!/bin/sh

# This is needed to pick up our built mono for commands
export PATH=/usr/local/bin:$PATH 

apt-get install build-essential bison gawk
apt-get install libglib2.0-dev
apt-get install libpng12-dev libx11-dev libfontconfig1-dev
apt-get install libfreetype6-dev libjpeg62-dev libtiff4-dev
apt-get install libungif4-dev libexif-dev libcairo2-dev
apt-get install libpango1.0-dev libgtk2.0-dev libglade2-dev
apt-get install libgnome2-dev libgnomecanvas2-dev libgnomeui-dev
apt-get install libgnomeprint2.2-dev libgnomeprintui2.2-dev
apt-get install libpanel-applet2-dev libgtksourceview-dev
apt-get install libgtkhtml3.14-dev

BUILDDIR=~/mono-build
mkdir -p $BUILDDIR
cd $BUILDDIR

wget http://go-mono.com/sources/libgdiplus/libgdiplus-1.9.tar.bz2
tar xvf libgdiplus-1.9.tar.bz2
cd libgdiplus-1.9
./configure --prefix=/usr/local
make
make install
cd ..

wget http://go-mono.com/sources/mono/mono-1.9.1.tar.bz2
tar xvf mono-1.9.1.tar.bz2
cd mono-1.9.1
./configure --prefix=/usr/local
make
make install
cd ..

wget http://switch.dl.sourceforge.net/sourceforge/nant/nant-0.86-beta1-src.tar.gz
tar xvf nant-0.86-beta1-src.tar.gz
cd nant-0.86-beta1
make install --prefix=/usr/local
cd ..

wget http://go-mono.com/sources/gtk-sharp210/gtk-sharp-2.10.4.tar.bz2
tar xvf gtk-sharp-2.10.4.tar.bz2
cd gtk-sharp-2.10.4
./configure --prefix=/usr/local
make
make install
cd ..

wget http://go-mono.com/sources/gnome-sharp2/gnome-sharp-2.16.1.tar.gz
tar xvf gnome-sharp-2.16.1.tar.gz
cd gnome-sharp-2.16.1
./configure --prefix=/usr/local
make
make install
cd ..

wget http://go-mono.com/sources/gtksourceview-sharp2/gtksourceview-sharp-2.0-0.12.tar.bz2
tar xvf gtksourceview-sharp-2.0-0.12.tar.bz2
cd gtksourceview-sharp-2.0-0.12
./configure --prefix=/usr/local
make
make install
cd ..

cd mono-1.9
wget  http://go-mono.com/sources/monodoc/monodoc-1.9.zip
unzip monodoc-1.9.zip
cd monodoc-1.9
./configure --prefix=/usr/local
make
make install
cd ../..

wget http://go-mono.com/sources/mono-tools/mono-tools-1.9.tar.bz2
tar xvf mono-tools-1.9.tar.bz2
cd mono-tools-1.9
./configure --prefix=/usr/local
make
make install
cd ..

wget http://ftp.novell.com/pub/mono/sources/mono-debugger/mono-debugger-0.60.tar.bz2
tar xvf mono-debugger-0.60.tar.bz2
cd mono-debugger-0.60
./configure --prefix=/usr/local
make
make install
cd ..

wget http://ftp.novell.com/pub/mono/sources/heap-buddy/heap-buddy-0.2.tar.gz
tar xvf heap-buddy-0.2.tar.gz
cd heap-buddy-0.2
./configure --prefix=/usr/local
make
make install
cd ..

wget http://ftp.novell.com/pub/mono/sources/mono-addins/mono-addins-0.3.1.tar.bz2
tar xvf mono-addins-0.3.1.tar.bz2
cd mono-addins-0.3.1
./configure --prefix=/usr/local
make
make install
cd ..

wget http://ftp.novell.com/pub/mono/sources/monodevelop/monodevelop-1.0.tar.bz2
tar xvf monodevelop-1.0.tar.bz2
cd monodevelop-1.0
./configure --prefix=/usr/local
make
make install
cd ..

Popularity: 100% [?]

Passing of Gygax

Friday, March 7th, 2008

I remember very vividly being introduced to D&D at the age of 12.  A friend of mine from junior high, Travis Dudley, had all five 1st edition boxed sets.  Our first coupld of days with them were mostly creating level 55 characters and fighting Dragons.  Not high art, but incredibly fun.

A year later 2nd Edition came out, and we started being interested in things besides just battles with dragons.  I remember getting issues of Dungeon Magazine, and DMing adventures out of there.  D&D lead off into other RPGs, like the teenage mutant ninja turtles universe, shadow runner, cyberpunk, and a range of table top games.  It even lead to my friend Chris and I writing our own table top game (85 page rule book), that had 3 generations of game play mechanics at the age of 15.  Yes, at 15 we spent our spare hours writing an 85 page rule book, printing out drafts on my dot matrix printer (which took about 3 hrs), and doing play testing and editing.

But it all started with cracking open Gygax’s D&D and a realization: with a few well crafted rules your imagination can take you to places you never imagined.

RIP Gary.

Popularity: 16% [?]

Graphing with Gruff

Sunday, February 17th, 2008

The house monitoring project has made a little bit of progress, as I’ve now got data being collected into a rails app using backgroundrb, and can get that data back out into very pretty graphs with gruff.  (I also looked a little bit a sparklines, but that’s specifically for graphs without labels.)
 

We’re running above target temp as it’s the weekend, so the wood stove is on.  As is the furnace fan to spread the heat through the house.  I’m overloading the values for heat on an fan on to be either the bottom of the graph or a specific small value.  I need to sort out a better way to put that into the graph, which may require some hacking on gruff itself.

Popularity: 25% [?]

Early working thermostat code

Sunday, February 17th, 2008

I definitely don’t have this anywhere near I like it, but I did manage to just dump out a bunch of info from my thermostat and turn off the fan with this script:

#!/usr/bin/ruby

require ‘thermostat’
require ‘pdp_constants’
include Proliphix

t = Thermostat.new("192.168.1.30","admin","XXXXXXXX")
t.set_senors(ThermHvacMode, ThermHvacState, ThermFanState, ThermFanMode, ThermAverageTemp, ThermHeat1Usage)
t.fetch_data

# dump out what we have
puts t

# turn off the fan
t.set_data(ThermFanMode, 1)
 

I need some nicer symbolic constants for state setting, and pull together a rails site just to keep track of thermostat data over the course of the day.  All this code is going on my newly registered rubyforge project.

Popularity: 21% [?]

The Earth in OpenSim

Friday, February 15th, 2008

Xplanet is this great program on Linux that does projections of the earth (including all kinds of possible overlays to include things like real time clouds, earthquake activity, and major storms.)  For years I’ve used it as the background image on my Linux desktop.  On the way home last night, while chatting with my friend Trey, it occured to me that through creative use of OpenSim, we could easily do this this in world with dramatic effect.

Below is a couple of screen shots of my 10m globe.  I had to modify the code paths for osSetDynamicTextureURL to get 1024×1024 textures into the pipe, as 512×512 doesn’t actually look good wrapped on a 10m globe.  There is still a bit of work to make this really good (like completing osSetDynamicTextureURL so we don’t need another timing loop in LSL to do refresh), but the initial results are quite nice. :)

The circles with numbers are earthquakes in the last 24 hours plus magnitudes (this information comes from the TotalMarker project).

Here you can see the storm track of the named storm Ivan (also from TotalMarker).

Popularity: 21% [?]

Thoughts on a smarter home

Sunday, January 13th, 2008

While our new woodstove insert is really great, and is definitely reducing our oil usage, it causes a bit of an issue when it comes to distributing the heat through the house.  When running on wood heat we get warmth right up the center of the house.  The office at the end of the upstairs hallway ends up being the warmest room in the house.  It is far too easy to make the upstairs unlivably warm, while the rest of the house is quite cool still.

Fortunately, we have a central air HVAC system, so the solution is to just turn on the furnace fan to redistribute the heat through the house.  This works pretty well, and at least mellows out the hot spots.  Ideally we wouldn’t run the fan all the time, but would duty cycle it on for some portion of an hour.  Honeywell makes a thermostat that has a cyrc fan mode, which runs the fan 35% of each hour, which was an option, but something else caught my eye.

The Proliphix Internet Thermostat NT20e is quite a nifty device.  It has a bit more programing than our current thermostat and has the advantage of having a web interface.  You plug the device into your ethernet network and get a web interface for all the controls and programming for the device.  What’s even better is that Proliphix designed this with further customization in mind by publishing an HTTP API to the device as well.  This makes is very easy to have a computer create further logic for the device, like forcing the fan on for certain hours of the day, while leaving the defaults for programming in the device itself.

I’ve now ordered mine, and it is on it’s way.  I can’t wait to get this thing hooked up.

Popularity: 13% [?]

More fun with dbus

Sunday, December 2nd, 2007

Since my dbus post last week, I’ve been playing around more with dbus whenever I get a few minutes. The modern Linux desktop is pretty good, but with minor tweaks, you can make things even better. (all this code is now up in a mercurial repository called dbus-hackery).

Automating Inactivity

Pidgin makes sounds on every message to me, xchat makes sounds on certain key words. Without these cues, I’d never remember to go check these applications. Because I have a tendency to leave my laptop on overnight, I found that I’d often have xchat ringing away at midnight when someone was looking for me. If I forgot to mute my machine before that, it would often wake me up.

One of the programs sending signals on dbus is gnome-screensaver.

def connect_screensaver(session_bus)
    ss_dbus = session_bus.service("org.gnome.ScreenSaver")
    ss = ss_dbus.object("/org/gnome/ScreenSaver")
    ss.introspect
    if ss.has_iface? "org.gnome.ScreenSaver"
        ss.default_iface = "org.gnome.ScreenSaver"
        puts "Connected to screensaver"
    end
    return ss
end

def mute()
    IO.popen("aumix -vq") {|r|
        r.read.scan(/(\d+)/) {|m|
            @@vol = m
            puts "saved volume: #{@@vol}"
            break
        }
    }
    puts "muting"
    system("aumix -v 0")
end

def unmute()
    puts "unmuting"
    system("aumix -v #{@@vol}")
end

ss = connect_screensaver(session_bus)

ss.on_signal("ActiveChanged") {|s|
    if s
        mute
    else
        unmute
    end
}

The connect will look exactly as expected from the previous look at dbus. The ActiveChanged signal outputs a single parameter, a boolean, which is true when the screensave goes active, false when the screensaver is deactivated.

Volume control on the command line is most easily done with aumix (though if you are on Ubuntu Gutsy you’ll have issues until you rebuild aumix yourself. Hopefully they’ll fix that bug soon.) A little regex fun captures the current levels to a package variable, and restores them back on unmute.

Now I’ve got global mute when the screensaver fires, restored when I return.

Better Away with Pidgin

My screensaver being locked is a pretty clear indication that I’m away, though it being unlocked isn’t a clear indication that I’m back. Especially on weekends, I pop back for a quick check of something, then the computer is put away again.

def set_away(pidgin)
    puts "trying to set away"
    name = "screensaver"
    status = pidgin.PurpleSavedstatusFind(name)[0]
    if not status > 0
        status = pidgin.PurpleSavedstatusNew(name, 5)[0]
    end
    puts "Status #{status}"

    pidgin.PurpleSavedstatusSetMessage(status, "screen saver auto away")
    pidgin.PurpleSavedstatusActivate(status)
end

ss.on_signal("ActiveChanged") {|s|
    if s
        mute
        set_away(pidgin)
    else
        unmute
    end
}

In order to set a status with a message, it has to be a saved status. To prevent growing that to infinity, I first look to see if it is defined, creating a new saved status if not. 5 is a magic number here meaning STATUS_AWAY (reference the pidgin status.h for more info). Then we set the message on that status, and activate it. A single line change on our screen saver signal adds this into play.

Keep on Hacking

One of the things I’m hoping to impress in these posts on dbus is that with a highly functional language like ruby, linking applications on a modern gnome desktop can be done even by mere mortals. Linking sound to your screen saver is something that would have required a reasonable chunk of c code. Now you can do it in 20 lines of ruby, thanks to dbus.

I’ve thought about creating some sort of extended control panel to enable the features I’ve hacked together, but the reality is the code is so small, and so simple, it seems like overkill. With code this easy, you should just jump in and hack it to your own needs.

As I keep playing with dbus, I’ll post more bits here. Twitter integration is still on my list of things to do, and maybe something I’ll even manage to get to this week.

Popularity: 24% [?]

Pidgin, Network Manager, Dbus, Ruby, oh my!

Sunday, November 25th, 2007

A few weeks ago I was chatting with Dan about my one great annoyance with Pidgin: it takes up to 15 minutes to realize that it’s network connection isn’t valid any more and to automatically reconnect. With NetworkManager and dbus, this should be a reasonably simple feat. Dan got curious about this and wrote himself a python program that sets pidgin status in this way.

D-What?

A bit of background. One of the Freedesktop.org standards is d-bus:

D-Bus is a message bus system, a simple way for applications to talk to one another. In addition to interprocess communication, D-Bus helps coordinate process lifecycle; it makes it simple and reliable to code a “single instance” application or daemon, and to launch applications and daemons on demand when their services are needed.

The basic idea is that d-bus allows applications to publish interfaces that other applications on the desktop can interface with. Right now, only a few applications really support d-bus in a significant way. Fortunately, one of those is pidgin, which has an extremely rich d-bus interface.

Ruby & DBUS

Here’s a place where I know I’ll draw Dan’s ire. I’ve been on a Ruby kick recently, so my first reaction to Dan’s post was “great, I’ll have to figure out how to do this in ruby now.” It sounds like typical language bias, but I’ll try to justify it at least a little.

Right now I’m writing code on a weekly basis in 3 languages: C# (OpenSim), Java (Grad School Project), and Ruby (side web projects done in Rails). 3 languages is a lot to remain fluent in, and causes some interesting syntax errors when jumping back and forth between them. One less context switch seemed like a good idea. At some point I’ll bother writing up why ruby has seduced me, but that is for another day.

While finding python-dbus bindings are extremely straight forward, the ruby-dbus front is a little more of a wandering path. After finding a couple of abandoned efforts, I finally came to the active ruby-dbus project. Make sure to grab the latest and greatest code from there before proceeding.

Connecting to DBUS

Before I get into actually using dbus, I need to set up connections to it. There are 2 different buses, the system bus (which is shared for all users), and the session bus, which is unique per login session. Pidgin uses the session bus: it’s a user application. Network Manager uses the system bus: it’s events are system wide and affect all users.

#!/usr/bin/ruby

require “dbus”

bus = DBus::SystemBus.instance
session_bus = DBus::SessionBus.instance

# Get the Pidgin Service
pidgin_dbus = session_bus.service(“im.pidgin.purple.PurpleService”)

# Get the object from this service
pidgin = pidgin_dbus.object(“/im/pidgin/purple/PurpleObject”)

# Introspect it
pidgin.introspect
if pidgin.has_iface? “im.pidgin.purple.PurpleInterface”
    pidgin.default_iface = “im.pidgin.purple.PurpleInterface”
    puts “We have Pidgin interface”
end

n_dbus = bus.service(“org.freedesktop.NetworkManager”)
netman = n_dbus.object(“/org/freedesktop/NetworkManager”)
# Establish a proxy interface object for NetworkManager as it doesn’t support introspection
poi = DBus::ProxyObjectInterface.new(netman, “org.freedesktop.NetworkManager”)

Basically the pattern is clear. Get a service handle, get an object definition from that service handle, then get an interface from that object. There are 2 flavors for this, one where we’ve got introspection information, and one where we’ve got to go blind because introspection isn’t supported.

DBUS Introspection

DBUS interfaces come in 2 flavors, those that support introspection, and those that don’t. If an interface supports introspection you can get an interface definition off the dbus wire itself, otherwise you need to know the interface a priori. Pidgin supports introspection, Network Manager does not. The major short coming of the ars technica article on Pidgin and DBUS was the lack of information on using introspection to show all the other pidgin interface functions (of which there are > 600). Python dbus introspection throws an exception on my Ubuntu 7.10 environment, so it wasn’t helpful here. However, ruby, as usual, came to the rescue.

The ruby-dbus code contains an example application called gd-bus, which performs introspection on all dbus interfaces it can find, and prints them out nicely. The cheat sheet goes something like so:

M PurpleAccountsFind(in name:s, in protocol:s, out RESULT:i)

  • M - DBUS Method, aka Function (might also be S meaning it’s a Signal you can register to listen to)
  • PurpleAccountsFind - the method name
  • in / out - whether this is an input or output parameter
  • NAME:type - the name and type of the parameter. Types can be s - string, i - integer, u - unsigned int, ai - array of integers

Note: the ruby interface seems to always wrap arrays around the output parameters. I have no idea why, but it’s consistent, so a few extra “[0]“s get you a long ways. If you know why, please comment.

The translation of this interface specification into ruby gives you something like this:

def recycle_pidgin(pidgin)
    accounts = pidgin.PurpleAccountsGetAll
    for account in accounts[0]
        if pidgin.PurpleAccountIsConnected(account)[0] > 0
            pidgin.PurpleAccountDisconnect(account)
        end
        pidgin.PurpleAccountConnect(account)
    end
end

This cycles through all pidgin accounts, disconnects all the connected ones, and then attempts to connect all accounts. This is effectively what I end up doing by hand every time I switch networks with my laptop.

Bringing it all together

I now had connections to the 2 buses, and code to cycle the pidgin accounts. Last bit is actually watching for the Network Manager signal that I’ve got a new active network device.

poi.on_signal(bus, "DeviceNowActive“) {
    recycle_pidgin(pidgin)
}

main = DBus::Main.new
main << bus
main.run

Network manager doesn’t support introspection. However, it does have pretty decent docs to figure out what the interface is. I’m still sad it doesn’t show up nicely in gd-bus though.

The proxy interface object sets up a signal using 2 parameters and a code block. Every time there is a DeviceNowActive signal on the system bus, I recycle pidgin. Pretty straight forward.

The last little bit is making this thing go into a loop. Ruby dbus contains it’s own main loop for just this task. I created a new main loop, tell it to watch the system bus, and then start it. And, we’re done.

The future’s so bright…

DBUS has been on my list of “I need to go figure this out” for a while. A morning of reading docs, hacking a bit, and crashing network manager a few times, and voila, you’ve got this blog post.

I’ve got lots of ideas floating around in my head now for other things that I can do with dbus to make my applications work better for me. As I bang a few of those out into code, expect to see more here about it. Pidgin is an especially target rich environment given how rich and interface they expose (nice job guys!).

Popularity: 25% [?]