Remote controlled Home Automation using Sinatra, Ruby and the LightwaveRF Wifi box

In the last post I created a Ruby Gem to communicate with the LightwaveRF Wifi Link Box. The next step was getting that gem into a web server so that I could send commands to the LightwaveRF box over HTTP. The eventual aim was to get this deployed on a RaspberryPi so that it could act as an always-on webserver to control any of the devices in the house.

The obvious choice for a lightweight and simple webserver in Ruby is Sinatra. The following code was all it took to get things started:

class App < Sinatra::Base
  def initialize
    super
    @lightwave = LightwaveRF.new
  end

  get "/" do
    body "Usage: turn items on or off: /[on|off]/:room/:device to dim /dim/:room/:device/:level. Level should be between 0 and 100."
  end

  get "/:room/:device/:action/?:level?" do |room, device, action, level|
    return [422, "Level required for dim"] if action == "dim" && !level
    case action
    when /on/i
      @lightwave.turn_on(room, device)
    when /off/i
      @lightwave.turn_off(room, device)
    when /dim/i
      puts level
      @lightwave.dim(room, device, level.to_i)
    else
      return [422, "Unknown action #{action}"]
    end
    body "Room #{room} Device #{device} #{action}#{" to #{level}%" if level}."
  end
end

The code starts off by creating an instance of the LightwaveRF gem. Then you can just call the root page on the server to see the interface. Then you can use the following to turn on room 1, device 2.

http://localhost:9292/on/1/2

or to Dim room 2, device 3 to 50% you could use:

http://localhost:9292/dim/2/3/50

Don’t forget you’ll need to register the device first by calling LightwaveRF.new.register from the computer that’s running the webserver.

If you’re not familiar with Ruby the following should help you get setup in the terminal assuming that you have ruby installed:

git clone git@github.com:scsmith/lightwaverf-sinatra.git
cd lightwaverf-sinatra
bundle install --path ./vendor/bundle
bundle exec ruby -e "require 'lightwave_rf'; LightwaveRF.new.register; puts 'Done'"
bundle exec rackup -p 9292

This will register the device and start the server running on port 9292 (the default for sinatra). You can then call http://localhost:9292/ like above to start using the server.

Advertisements

Controlling access to Routes and Rack apps in Rails 3 with Devise and Warden

Devise is a great authentication solution for use within your Rails apps. We’ve used it in a number of projects and have always been happy with the flexibility but never realised quite how great it was until very recently.

Recently we wanted to make use of an A/B testing solution and one of the options was Split. Like a number of projects nowadays (Resque included) Split comes with an embedded server that allows you to control it via the web interface.

You can mount that rack app using:

mount Split::Dashboard, :at => 'split'

The problem is this leaves split open for anyone to see and your AB testing is open for anyone to play with. The split guide recommends that you can use a standard rack approach to protect your split testing.

Split::Dashboard.use Rack::Auth::Basic do |username, password|
  username == 'admin' && password == 'p4s5w0rd'
end

We were already using Devise though and I’m sure this is only there to prove as an example by the authors. What we really wanted to do was use Devise to protect our mounted app. It turns out that this isn’t that difficult. Firstly you can authenticate any route using devise using the authenticated command like the following:

authenticated(:user) do
  resources :post
end

This ensures that the following route, specified with the block, is only availible to people who have authenticated. Using authenticate you can also bounce people to the login page if they’re not authenticated:

authenticate(:user) do
  resources :post
end

This is awesome and ensures that only registered users can access the routes. You can of course replace :user with any of your scopes, such as :admin, in order to ensure that only specific scopes have access to the route. However, we wanted a bit more control. What if you want to only allow any users that had the boolean `allowed_to_ab_test?` method?

As soon as you realise that:

mount Split::Dashboard, :at => 'split'

is actually just a short hand for

match "/split" => Split::Dashboard, :anchor => false

You can use any normal constraint. Since warden makes use of the request object you can pull out any details you like from warden including the user model by using a lambda for the constraint. This provides massive flexibility for controlling your routes:

match "/split" => Split::Dashboard, :anchor => false, :constraints => lambda { |request|
  request.env['warden'].authenticated? # are we authenticated?
  request.env['warden'].authenticate! # authenticate if not already
  # or even check any other condition
  request.env['warden'].user.allowed_to_ab_test?
}

With that example we can take a really fine grained control of our routes and only allow access to certain routes for certain people without having to specify different scopes for the users.

Nothing here’s groundbreaking but it took us a little time to figure it out. The combination of Devise and Warden gives a huge amount of power and flexibility allowing you to protect both your normal routes or your embedded apps. We thought we’d share this in case it was useful to others. It’s certainly opened our eyes to just how great Devise and Warden are!

Multipart Body – A gem for working with multipart data

Multipart queries are used quite a lot in the transfer of data around the Internet. There are a number of projects out there that will generate multipart content such as email libraries and even web frameworks for uploading and working with files. When we came create parts of CloudMailin we couldn’t find a gem that would easily allow us to encode multipart content the way we wanted to. We could have used a library that already this ability it baked in but most of them didn’t work with eventmachine and if they did then we couldn’t be sure that they would work with any testing tools that we created later that didn’t rely on eventmachine. Although loads of libraries were implementing this code we couldn’t find anything that was standalone that we could just use across any of the different libraries that could post content.

In order to solve this issue we created our own internal multipart creation code. This weekend we have released that code as a gem called multipart_body. This gem is far from perfect and we have a list of things that we don’t have time to add and we would love some help with but the code has been useful to us so we hope it will be useful to others too.

The gem itself consits of two parts. Multipart body and the parts. To get started just install the gem


$ gem install multipart_body

Once the gem is installed you can create a form-data multipart body using this quick hash shorthand.

require 'multipart_body'
multipart = Multipart.new(:field1 => 'content', :field2 => 'something else')

To get a little more control you can create the parts yourself and use them to create the body:

# using a hash
part = Part.new(:name => 'name', :body => 'body', :filename => 'f.txt', :content_type => 'text/plain')

# or just with the name, body and an optional filename
part = Part.new('name', 'content', 'file.txt')
multipart = Multipart.new([part])

You can also pass a file to the multipart hash to automatically assign the filename:

require 'multipart_body'
multipart = Multipart.new(:field1 => 'content', :field2 => File.new('test.txt'))

The resulting output can then be created as follows:

part.to_s #=> The part with headers and content
multipart.to_s #=> The full list of parts joined by boundaries

So the following code example will create the output that follows:

multipart = MultipartBody.new(:test => 'content', :myfile => File.new('test.txt'))
------multipart-boundary-808358
Content-Disposition: form-data; name="myfile"; filename="test.txt"

hello
------multipart-boundary-808358
Content-Disposition: form-data; name="test"

content
------multipart-boundary-808358--

Like I said before the gem is far from perfect. At the moment it doesn’t have any documentation and it is missing quite a few features. By default it assumes you are creating form-data content and encodings are completely missing at the moment.

Hopefully though with a little bit of help it can provide a great starting block for anyone wishing to implement multipart bodies so that each library doesn’t have to re-invent this. If anyone has any time I’d love to see patches to bring this up to something much more useful.

Render ‘Rails Style’ Partials in Sinatra

We love Sinatra. Not only does it make a great framework in its own right but in addition it can be used to mimic parts of rails in a real simple environment for front-end designers. Instead of having to get them set up and explain the whole of rails they just get a nice simple app to work on without having to worry about creating different controllers or even models.

Although there is not a 1 to 1 translation between a rails app and a sinatra one, it does allow these developers to work with things like haml in a really easy to work with environment.

One of the features that I was asked for recently though was “How do you render a partial in sinatra?”

Rendering Partials in Sinatra

Sinatra is a super-lightweight framework. Because of this it doesn’t have the notion of partials built into it. However, a partial, in its simplest form, is nothing more than a call out to render the template as a string and then embed that string into your page.

A quick look at the sinatra sites FAQs shows that partials can be rendered in the following way in erb.

<%= erb(:mypartial, :layout => false) %>

In haml you could use exactly the same thing but call haml like so.

= haml(:mypartial, :layout => false)

Notice that

:layout => false

is set to ensure that the layout is not also rendered.

Going a little further

The FAQs also recommend using the code in the following gist.

http://gist.github.com/119874

The code shows a helper method called partial. This helper method can be used to render a partial from your code. The helper also allows you to pass collections and is a really cool and useful piece of code.

Making things work the rails way

The above helpers are great and really useful for sinatra. However, what if you want to render a partial the ‘rails way’? In our situation we were using sinatra as a mock up of what would eventually be brought into a rails app. Rails allows partials to be included like so:

<%= render :partial => 'partial_name' %>

By overriding the built in render method in Sinatra it is actually possible to mimic the rails partials. I came up with the following helper to quickly mock things up. The helper checks to see if the first argument passed to is a hash and if that contains they key :partial. If so it renders the partial, if not it just uses the default render method.

  helpers do
    def render(*args)
      if args.first.is_a?(Hash) && args.first.keys.include?(:partial)
        return haml "_#{args.first[:partial]}".to_sym, :layout => false
      else
        super
      end
    end
  end

The helper could easily by extended to allow for collections etc but for now it does the job. Any better solutions?