Friday, September 8, 2017

fixing Hue persistence using async IO in Rust / Futures

Intro


I have two Philips Hue lamps and mostly like them when  they are in the very warm white (2000k) color temperature setting.
The problem is that the setting does not persist when the lamps are switched using a physical power switch. To solve this I wrote the Hue Persistence program last year.
This program runs on the LAN and basically polls and stores the lamp settings and when it it detects a lamp going from off to on it restores the last setting.

After using it for a few weeks it became clear that there is one big problem with it: the Philips Hue hub sometimes accepts a connection from my program but then does not respond, yet keeps the connection open.
As the program is written using synchronous IO, this causes the program to hang forever, and it doesn't work anymore.

Solution


The Hue Persistence program uses the Rust Philips Hue library which using Hyper as a HTTP client to talk to the Philips Hue hub via it's restful API.
This library still uses Hyper 0.10 which does not provide async IO yet.

Recently there has been a lot of work for asynchronous IO in rust using Tokio and Futures. Version 0.11 of Hyper uses this.

First thing I had to do is port the Rust Philips Hue library to use Hyper 0.11 and thus async IO. This turned out to be quite an undertaking. The result of this can be found on the hyper_0.11 branch of my fork of the library. It is still a bit ugly, but works.

So far I've found using Futures for async IO to have the following issues:

  • error handling can be very confusing; error types need to be the same in future composition and you have to explicitly do error type conversions to make things work
  • future composition results in complex types, returning to Box<Future> helps a bit, but it still can be rather confusing (I didn't try the not yet released impl Trait solution)
  • it's not so obvious which future composition methods to use when, docs are sparse
  • branching (if, ...) inside futures is tedious again because of types having to match perfectly
In the end though I got the satisfaction of a working async library, after which I updated the Hue Persistence app to have a timeout on it's requests. This again turned out to be really tedious.
I had to use select2 with a sleep instead of tokio_timer::Timer::timeout() because it was just impossible to get the types to work for the error types when using tokio_timer::Timer, due to the TimeoutError containing Future type as a containing type in the signature of timeout().

Conclusion


My lamps are behaving fine now!

Async IO in Rust using Futures/tokio is interesting but at this point it still feels like "getting the types right" is somewhat in the way of normal coding productivity.
Some ergonomic improvements/language support could surely be used.
To be revisited in the Future (pun intended ;) ).



Saturday, June 17, 2017

implementing a simple language switch using rocket in rust

Let's look how easy it is to implement a simple cookie based language switch in the rocket web framework for the rust programming language. Defining the language type:

In this case there will be support for Dutch and English. PartialEq is derived to be able to compare Lang items with ==.

The Default trait is implemented to define the default language:

The FromStr trait is implemented to allow creating a Lang item from a string.

The Into<&'static str> trait is added to allow the conversion in the other direction.

Finally the FromRequest trait is implemented to allow extracting the "lang" cookie from the request.

It always succeeds and falls back to the default when no cookie or an unknown language is is found. How to use the Lang constraint on a request:

And the language switch page:

And as a cherry on the pie, let's have the language switch page automatically redirect to the referrer. First let's implement a FromRequest trait for our own Referer type:

When it finds a Referer header it uses the content, else the request is forwarded to the next handler. This means that if the request has no Referer header it is not handled, and a 404 will be returned.
Finally let's update the language switch request handler:

Pretty elegant. A recap with all code combined and adding the missing glue:

Sunday, January 29, 2017

making a HP Color LaserJet printer wireless with a Raspberry Pi 3



My old HP Color LaserJet 2820 is still working great, however it only supports being connected via ethernet or USB. As a workaround I've been using "Power Line Communication" to bridge directly from my router to the printer over the electrical wires.
Recently I've been revamping the home network and wanted to get rid of these last power line connections. Ideally I'd want the printer to join the wireless network.

The arrival of the Raspberry Pi 3 with builtin WiFi seems like a good opportunity to implement this.
The raspberry Pi is connected with it's Ethernet port to the printer. The WiFi is connected to the local LAN where I configured the router to give it a fixed IP address.

Approach 1: Cups printer server


I install the cupsys printing system on the Raspberry Pi, and configure it to properly support the printer (using HPLIP). Besides that, I install a DHCP server to serve the printer an IP address on it's own private network: The cable between Raspberry Pi and printer.
I change the configuration of the printer in Cups on the Raspberry Pi to make the printer shared on the LAN.
Now all other computers can just use that shared printer to print.
This works really good for printing, however it doesn't work at all for scanning.
When scanning the scanning software (SANE) seems to want to connect directly to the printer and this of course isn't possible when the printer is on a different network and there is no routing.

Approach 2: Routing


I updated the Raspberry Pi to do routing, by enabling IP forwarding.
(uncomment the net.ipv4.ip_forward=1 line in /etc/sysctl.conf )
I also configure my router with an extra route to have it know about the network that is now reachable via the Raspberry Pi.
Now the printer can be directly communicated with. This allows me to configure the printer directly again on my Linux laptop.
Printing still works fine. However scanning still doesn't work.
When I tell the scanner software the IP address of the printer explicitly, it sometimes works, but most of the time it doesn't work at all.
My guess is that the scanner software relies on some communication that doesn't work outside it's own network.

Approach 3: Proxy ARP

To learn more about the technicalities of proxy ARP, read here on Wikipedia, and here on wiki.debian.org. In summary it is a trick that makes it seem to the rest of the network that the Raspberry Pi and the printer are the same.
In order for this to work I want the printer to have an address in the same network as the local net.
First remove the DHCP server installed for the previous approaches, then install a DHCP proxy. This is easily installed (apt-get install dhcp-helper) and configured (DHCPHELPER_OPTS="-b wlan0" in /etc/default/dhcp-helper).
There are multiple ways to enable Proxy ARP. The easiest is to install (apt-get install parprouted) and configure it.
My /etc/network/interfaces now looks like this:

auto lo
iface lo inet loopback
auto eth0
allow-hotplug eth0
iface eth0 inet manual

allow-hotplug wlan0
iface wlan0 inet dhcp
  wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
  post-up /usr/sbin/parprouted eth0 wlan0
  post-down /usr/bin/killall /usr/sbin/parprouted
  post-up /sbin/ip addr add $(/sbin/ip addr show wlan0 | perl -wne 'm|^\s+inet (.*)/| && print $1')/32 dev eth0
  post-down /sbin/ifdown eth0


With proxy ARP in place the printer still works great, and finally the scanner also works in a stable way.


Alternative approaches


I could try hooking the printer to the Raspberry Pi via USB. As I've never used the printer in this way, I've not attempted this.

Tuesday, January 10, 2017

stable wifi on the shuttle xs36v with Debian

The Wifi connection of this machine is very slow, especially noticeable when logging in to the machine via ssh.
The solution is to follow the notes on https://wiki.debian.org/rtl819x , copied here:

Devices supported by this driver may suffer from intermittent connection loss. If so, it can be made more reliable by disabling power management via module options ipsand fwlps. These can be supplied by creating a new file:
/etc/modprobe.d/rtl8192ce.conf


  • options rtl8192ce ips=0 fwlps=0


Thursday, January 5, 2017

Dirt Rally Timing Log

In order to keep track of my skill evolution playing the excellent rally sim game "Dirt Rally" I've set up a google sheet.

This google sheet could be interesting for other people as well so I'm sharing it here. Note that this one has some example content only not my actual full content.

To use it make a copy of it on your own drive.

Have fun driving!

https://docs.google.com/spreadsheets/d/10PBtlXl5hZIIYa_ab8ypG8jnvuQcaN2hbM9BMtDsqmw/edit?usp=sharing