In-depth how-to: Integrating Dash Buttons with SmartThings

After quite a bit of iteration, I’m mostly happy with the way I’ve integrated Dash buttons into my home automation setup. Here’s a demo:

My goals were:

  1. Make the buttons as responsive as possible.
  2. Make it robust.
  3. Setup should survive reboots and power outages without manual intervention.
  4. Integrate with SmartThings.

In a previous post, I outlined two different approaches. I went with the approach that had the lowest latency (<1s). This one is quite a bit more work — mostly because it requires a dedicated wireless card.

Here’s the equipment I used:

  1. Raspberry Pi 2 (I used the CanaKit starter kit. If you’re buying now, you’d probably want the Pi 3 edition).
  2. Edimax EW-7811Un USB WiFi dongle.

Important! to use this approach, you need at least one WiFi dongle that supports monitor mode. The Edimax dongle I suggested doesn’t support monitor mode, but the one that comes with the CanaKit 2 does. Note that the Pi 3’s onboard WiFi device does not support monitor mode, so you’ll want to buy a dongle that does (you can buy the CanaKit dongle separately for $9).

Set up dash buttons

This approach will work with the normal setup process, but with a slight modification, you can ensure that the dash buttons don’t contact Amazon when pressed.

The only thing you need to do differently is set up the buttons on a network you can delete later. I have dd-wrt on my router, so I used a virtual interface. If your router supports a “guest network” or something to that effect, it’s the same thing.

Create the network, set up the dash buttons on it, delete the network. The buttons will still attempt to connect when pressed, but won’t be able to because it doesn’t exist.

Install required packages

Set up the network

If you’re using ethernet + a WiFi dongle, you shouldn’t need to do much of anything. If you’re using two WiFi devices, it’s a little trickier. In order for this to work consistently across reboots, you’ll have to:

  1. Make sure that the interfaces (wlan0, etc.) are named consistently. They seemed to randomly swap by default, which obviously caused some problems.
  2. Tell the OS which device should be connecting to the network.

(1) is easy enough with ifrename. There’s probably a way to do it with udev, but this is way easier. It allows you to assign names to interfaces based on hardware (MAC) addresses. Open up /etc/iftab  in your favorite editor (just create it if it doesn’t exist). Mine looks like this:

After a reboot, you should see that the devices are named appropriately:

Notice you can name the interfaces whatever you want. monitorwan and mainwan seemed more informative than wlan0 and wlan1. 🙂

(2) is also pretty straightforward. There might be an easier way to do this, but I just did it by editing /etc/network/interfaces to my liking:

The wpa-psk field is a pre-shared key generated from your network SSID and passphrase. You can generate it with the  wpa_passphrase tool (from the wpasupplicant package):

You can apply these settings with a  sudo service networking restart . Probably good to reboot to make sure it works as expected.

Download ha_gateway

This setup uses ha_gateway, which is a small REST gateway I use to bridge a bunch of custom hackery with the rest of my home automation setup (mostly SmartThings). To install it, just check out the project from Github:

While I haven’t tested ha_gateway with anything but ruby 2.3.1, it probably works with 1.9+. If you’re getting errors when running bundle install , post a comment and I’ll help debug.

Create the monitor interface

In order to use monitor mode, we create a virtual monitor interface. We can do this with the iw tool, but I stuffed all of the setup into a script shipped with ha_gateway. It takes two arguments: the interface you’re using for monitor mode, and what you want to name the virtual interface

This should create an interface called DashMonitor :

To test if it’s working, you can try a tcpdump:

If you have basically any WiFi traffic around you, you should see packets pretty much immediately. If you don’t, it either means the monitor device isn’t working, or you’re legitimately not seeing traffic on whatever channel the NIC is tuned to.

To make sure the monitor device survives reboots, you can invoke the same script from /etc/rc.local :

Figure out MAC addresses of your dash button(s)

The easiest way I’ve found to do this is to use the monitor mode NIC and search for packets associated with the network you set them up on. I set my dash buttons up on a network called CMDashButton:

The hardware address is shown after “SA:” prefix.

Configure ha_gateway

ha_gateway is configured using a central YAML config file. You can just copy from the example:

Ignore the stuff at the beginning and skip down to the  listeners: key. You’ll create a listener for each dash button you want to use:

This will fire an HTTP PUT request to http://google.com/some/path with the specified params every time the button is pressed. We can worry about making it do something useful later. First, let’s verify the button presses are getting picked up.

Use the  run_listeners.sh script to fire up the ha_gateway listener process. Note you’ll have to run it with sudo — it won’t be able to listen on the monitor interface otherwise:

After waiting 10-20 seconds, press your dash button. You should see a log message that looks like this:

This means ha_gateway is successfully detecting dash button presses! Now let’s make it do something useful.

Integrating with Smart Things

ha_gateway integrates with SmartThings. We’ll be able to control your existing ST devices and routines with the dash button. Getting this working is a little complicated because SmartThings requires clients to oauth with it. Let’s get that out of the way first.

First, you’ll have to install ha_gateway’s SmartApp. Log into your ST account (https://graph.api.smartthings.com/) and click on “My Smart Apps”. Click on the green “New SmartApp” button on the right near the top. Click on the “From Code” tab and paste in this code:

This should take you to an editor page. Couple of things to do to finalize setup:

  1. Publish the newly created app – click on “Publish”, then “For Me”
  2. Click on the “App Settings” button, then click on the “OAuth” section.
  3. Click on the “Enable OAuth for this SmartApp” button.
  4. You should see two text fields containing a  “Client ID” and a “Client Secret”. Make note of ’em.
  5. Click on “Update” near the bottom. OAuth settings won’t persist if you skip this!

Copy the client ID and client secret into ha_gateway’s config YAML:

Setting site_location: is important so that the OAuth redirect ends up hitting the Pi again. For now, also make sure that require_hmac_signatures:  is set to false. It’ll make it easier to go through the OAuth process.

Now fire up the ha_gateway web server by running  bin/run.sh . Now navigate to:

http://ip-of-your-pi:8000/smartthings/authorize

This should direct you to an OAuth page on ST’s site. Select a hub, check the switches you want to allow control of, and click “Authorize”. You’ll be redirected to and endpoint that outputs a JSON blob containing information about the devices you authorized, which might look something like this:

You can control each device via RESTful PUT requests. For example:

This would send the “toggle” command to “Xmas Tree”, which would turn it off since its previous status was on.

If you wanted to configure a dash button to switch on and off your Christmas Tree, you’d edit the listener config like so:

Notice we don’t need to provide the full URL, just the path. ha_gateway will assume we want to send the request to its REST server. It’ll fill in the URL specified in the site_location:  key.

You can start both the REST server and the listener process with the included start script. It’ll run the listener process as root, so make sure you’ve got an active sudo session (i.e., make sure it’s not prompting for a password):

Logs are in logs/ha_gateway.log  and logs/listeners.log .

You can also run routines. You can access /smartthings/routines to get a list of routines. To run a routine, send a GET request to /smartthings/routines/<routine_name>. Normalize routine_name to be all lowercase, remove non-alphanumeric characters, and replace spaces with underscores (e.g., “Good Night!” -> “good_night”).

Starting ha_gateway at boot

Obviously we want the REST server and the listener process to survive a reboot. This is pretty easy. I use monit because I already had it set up, but it’s probably more straightforward to just add this line to /etc/rc.local :

Make sure it appears above the exit 0  at the end of the script.

Securing it

If you don’t mind anyone on your network being able to access ha_gateway (and therefore turn off your Christmas Cheer), you can enable HMAC signatures. This will require anyone making a request to sign the request with a shared secret. Just edit the config file:

Conclusions

This works really well for me. It was way more work than I expected when I decided to look into hacking the dash buttons. I have five dash buttons for various uses, and they work very reliably. Adding new buttons is really straightforward.

Thoughts on Amazon Dash Button Hacks

Excited by the prospect of $5 hackable IoT buttons, I ordered a couple to toy around with and use in my home automation setup. I knew some folks had already figured out how to hack them, but hadn’t looked closely at how they were doing it until my buttons arrived.

Not surprisingly, the hacks are pretty hacky. There were two approaches I found:

  1. Listen for ARP packets sent from the dash button. Devices send ARP probes after associating with a network in order to determine whether their hardware (MAC) address is already in use. My casual experimentation showed these packets showed up 3-5 seconds after the button was pressed. This is the standard approach first outlined in Ted Benson’s medium post.
  2. Listen for probe request packets sent from the dash button. Devices wishing to connect with a particular network will send these out to kick off the process. These show up much sooner after pressing the button — usually within 1 second. Outlined by ridiculousfish here.

There are some not awesome things about both of these. Probably the worst is that you need to run something like tcpdump as root, and your network device has to be put in promiscuous mode (in the case of #1), or monitor mode (in case of #2). Setting promiscuous mode on a NIC instructs it to not ignore traffic that isn’t addressed to it (it normally does). Monitor mode is a different beast. It’s more like sniffing the waves out of the air. The card sits there without being associated with a network and can report any packets it hears on whatever channel it’s set to.

#2 has some particularly nasty downsides:

  • Need a dedicated wireless device that supports monitor mode. Since you’ll presumably need whatever box you’re working with connected, you’ll need another network device (second wireless device or ethernet).
  • Not all wireless devices support monitor mode. Fortunately, the dongle that came with my Raspberry Pi does. This post lists a few chipsets that support monitor mode.
  • Requires some networking setup. Creation of virtual monitor device feels kludgy and does not persist across restarts.
  • Much easier to spoof. As we’re listening to packets sent before the device is associated with a network, an attacker would only need the MAC address of your button. Probably not a good idea to use the buttons for much beyond switching your lights on and off. : )

Even with these, I’m pursuing #2 because:

  • Latency for #1 is just too high for most uses.
  • Since we’re listening to probe requests, we detect a button press before it associated with a network. This means we can set the buttons up on a fake network (I used a virtual interface in dd-wrt), and delete it when we’re done. Net effect is that buttons won’t be able to connect when they’re pressed, and won’t try to order Clorox Wipes when you try to turn on the kitchen lights.
  • This last benefit is mostly nice because Amazon was sending me a push notification every time I pressed a button to the effect of “Select an item to order to finish setup!”. Had to turn push notifications for Amazon off.

We’ll see how it turns out!

Riddlegate: automating apartment intercoms

If you’ve ever lived in an apartment building or gated complex, you’ve probably seen one of these things:

Apartment Intercom

A person dials some code to reach you, you make sure it’s someone you’re expecting, and you press some digits on your phone to grant access.

However handy this is, there are situations where it’s not very helpful. I found, for example, that delivery drivers would often arrive while I was in a meeting or otherwise away from my phone. I really wanted to be able to just give them a code that allowed them access.

Enter Twilio. It enables exactly this. You rent a phone number through them (very cheap — on the order of $1/month), which can be configured to call HTTP endpoints when it receives a call, SMS, etc.

I built a small application around this to automate my apartment’s intercom. The intercom is configured to call my Twilio number, which will interact with the application. I called it “Riddlegate” after that plotline in The Neverending Story with the sphinxes. It’s pretty simple and self-explanatory. Here’s a screenshot of the admin UI:

X30j6Vt

Now when I have a guest, I can give them instructions that don’t involve me being near my phone:

  1. Dial <number>
  2. Wait for tone
  3. Dial <passcode>

And Riddlegate will buzz them in!

A (hopefully-not-too-terse) setup guide is included in the Github README.

Security

Given that this controls access to your building/complex, security is an important concern. There are a few things Riddlegate does to improve security:

  1. Twilio signs all requests it sends with your API key. Riddlegate validates these signatures and denies access when it detects an invalid signature. This prevents a would-be attacker from brute-forcing your access code if they were to discover your endpoint URL.
  2. Admin area is password-protected. This is obviously only as secure as the password you choose. Also obviously better if you serve over HTTPS.

Security cameras: automatically recording and uploading footage when a door is opened

My last post detailed how to integrate a cheap IP cam with SmartThings. I briefly mentioned a SmartApp that took a picture when the door opened. This was pretty straightforward. I wanted to take it a step further and trigger recording when my door was opened (but no one is home). Beyond the obvious, I had the following requirements:

  1. SmartThings needs to be able toggle recording. The SmartApp should notify my household when it begins recording.
  2. Tampering with the camera shouldn’t destroy already captured footage.
  3. Footage should start uploading somewhere offsite as soon as possible.
  4. Control over uploaded footage.

I already covered (1) in my last post. My integration with the IP camera enables a scheduled recording feature, and configures this feature to always record. Switching off recording clears the schedule.

Uploading footage to S3

The camera I’m using already has builtin support to upload footage to an FTP server, which leaves everything but uploading to offsite storage.

Since it’s easy and cheap, I decided to upload footage to Amazon S3. I then needed a tool that:

  1. Watched for newly created files to appear in the directory my internal FTP server is pointed at. When a new file is detected:
  2. Immediately begin a streaming upload to S3. Since the file size isn’t known ahead of time, I made use of the multipart upload API, which allows for the breaking down of uploads into smaller (5MB) chunks.
  3. When the file is done being written to, complete the upload (i.e., tell Amazon the file is done being uploaded). This makes the file available on S3.

This seemed like a good fit for inotify, which allows for the monitoring of filesystem events. It’s possible to set up notifications that are triggered when, for example, a new file within a directory is opened, closed, or modified.

I didn’t find a tool that did exactly what I wanted, so I made a ruby library that did. I called it “s3reamer“. My sincerest apologies for being an awful portmanteau-ist. I run this on my home server:

This will automatically begin uploading to S3 when recording on the device is triggered. Here’s a snippet from the log file:

Uploading starts within a few seconds of recording being switched on. Most of that delay is waiting for the camera to begin uploading.

SmartApp to trigger recording when door opens

This part was pretty straightforward. Code below. This also sends a push notification when recording is switched on so my household knows and can react accordingly.

Integrating Foscam FI9821P with SmartThings

Motivated mostly by curiosity, I was recently in the market for a cheap IP camera. After a little bit of research, I settled on a Foscam FI9821P (I got mine for ~$45 as an Amazon Warehouse Deal). The app provided by Foscam is pretty nice, but I wanted to integrate it with my home automation setup as well. In particular, I wanted to accomplish the following:

  1. Secure access. Any communication with the camera should require some secure authentication mechanism.
  2. SmartThings integration. I wanted a device in SmartThings I could play around with.
  3. REST endpoint. Although I could probably get most of what I want done with SmartThings alone, I didn’t want to be bound to it.

SmartThings has a device type for cameras, so as long as there’s some way to access the camera within SmartThings, (2) is easy. In a previous post, I outlined a setup that uses HMAC to secure communication with smart home devices. I leveraged it in this project as well.

I should mention that I stumbled across some existing attempts at this, but nothing that would’ve given (1) and (3).

I put together this route for my home automation gateway, which accomplishes (1) and (3). With it, I can capture a snapshot and control some rudimentary functionalities of the camera. I can, for example, request a snapshot of what the camera is currently seeing simply by accessing this URL (with the appropriate security headers in place):

http://HA_GATWAY_URL/camera/foscam1/snapshot.jpg

You can see that there’s baked in support for multiple cameras (since the endpoint is scoped by a camera name). While I don’t anticipate buying more cameras, I figured adding support would make this project more generally useful.

To integrate with SmartThings, I created a virtual device (code embedded below). It allows me to request an image, shift the camera to one of three preset positions, and to start/stop recording. Here’s a demo of the interface:SmartThings Interface

This project was a lot of fun, and quite a bit easier than I was anticipating. My favorite thing this has enabled is a SmartThings SmartApp that signals the camera to take a picture when my front door opens. To avoid being too creepy, this only happens when no one is home. If I can muster the motivation, I’ll probably write a separate post about that.

Cheap alternative to Phillips Hue LED Strip

I have some RGB LED strips in my bedroom to light an area other lighting in the room doesn’t reach. The strips I bought were inexpensive, but they only interact with the included infrared remote. I wanted to be able to control these lights with SmartThings. There are a couple of ways you can do this:

Easy and spendy

Phillips has a bunch of products that integrate nicely with SmartThings. The obvious contender here is this guy. However, for this to work, you’d also need a Phillips Hue Bridge. In total, this is going to run you somewhere between $150 and $250, depending on how many feet of LED strip you want.

Partially because this seemed unreasonably expensive, but especially considering I’d already glued LED strips to my walls, this solution wasn’t appealing.

Cheap and complicated

Browsing around, I found a cheap ($30) LED controller advertising “WiFi” control (link):

It was exactly what I was hoping for. It has a tiny TCP server that allows network control. The official mobile app is actually quite good, but it doesn’t integrate with the rest of my SmartThings stuff. I toyed around a little bit and managed to reverse engineer the protocol. I put it in a rubygem, available here.

This allowed me to programmatically control the LEDs, but obviously still no integration with SmartThings. Fortunately, that wasn’t very hard either.

Design

The overall design looks something like this:

7VfLU/M2EP9rcmzGD/LgSEJoD/QbpmGm7VGxha1BtjyS8uKvZ2WtbCkGmg6kH4fmkEg/rVf7+O1uPEqX1eFXSZryd5FTPkqi/DBKb0dJcj25gm8DHC0wmVxboJAst1DcA2v2QhGMEN2ynKpAUAvBNWtCMBN1TTMdYE+Ch1c0pHDqe2CdET5E/2S5Li06T6Y9/htlRemuiafozIZkz4UU2xrvGyXpU/uxxxVxutCrQ2S3bn/EPaprSB0Y9CJEFQCSqj5S6CwLna/JLriSs/o5DNlGyJxKTyhdQSKlEKDIrKrDknKTTJeodJpF8SzJKZ0m82iS/2JV350r3oVX0hqN/bTK1OpU+uiSSHPIKW6F1KUoRE34qkcXbaKoUQlhX5S64rCMYQlmyeNfiLebv81mPIGt0kTqGynFHqCME6VY5uA7BlZaFdYcY8MJpT7wGWWU2MoMHxvIYBnBbQV9T8+kSyJUIxUVBQdasnCi2S40iCAXik6ue/RBMDAVSIoPpI6kWMG4dRqsSfhQnzsIFTG3O7HGCChfAhaenT3U5vzM/GNYdoRv0b/71e3qx+pxlExJ1UBC6o0yP7DnELdFznawLMxyKSDDgnOoATyE673zAbM8puyo1Awaxz3ZUP4gFNNMmJrdCK1NrS64OVh0bWEpuLCl5hpDr+OGs8I8q4UxVIFNz137gaQg4mmIonl0Zyj6BLx7S7MqSd7y1AjlRJUd2eGkMd5Uh8K06zETajZm0DzVGKwo9WbLN53rxkDqWHAuj4f8Qw1OBdIoadmaRPu+0c6w/ZVej71Gsbf4GhDp37AGdXqs+WO1BspEayrB6UHi9yXTdN2Qtjr3ELiwbVwiWvE8rLorbHReuGLX2v14TU+q80viNR/Eqy5YffgOcUq/U5yQv16chtOpzt8ZIkP4zMlED0ybo1YO1v3JpYYR1s+Hw6jlzDBtXl7cIPHT4rDPzqyQFKfJts4NRtZAjXsO1Tit/zD5vmKuuas8Kq0rIMljyerCTtGAVZzDH2Lz38Y1+IyLLShf/ISSnJyUpPPFL0kXWT/3cTK7QE3G6NAlivI/LzxsxB8Wng33/5X3Jl9g27/gWPH+tTVdvQI=

I’ll elaborate on each of these components in the following sections.

REST server

The TCP API works nicely, but I wanted to wrap it in something that’d be easier to interface with. I wrote a really small REST gateway using sinatra. This serves two functions:

  1. Easy access. Obviously, integrating directly with a TCP server kind of sucks in comparison to making a REST call.
  2. Security. I added a before block in the sinatra to verify HMAC codes computed using a shared secret. This prevents unauthorized parties from using this server. Wouldn’t want randos turning my lights off and on!

This little server listens for requests on port 8000.

nginx

I use nginx as the externally facing endpoint because I have a bunch internal webservers, and nginx makes it easier to manage all of them. It also adds the ability to address the webserver using a subdomain instead of a custom port. The config looks like this:

Notice the server is listening on port 81. My router opens port 80, and forwards it to port 81 on my home server. I do this because internal services I don’t want to expose to the outside world run on port 80, but I’d prefer to use port 80 from the outside world. The request chain looks something like:

Integrating with SmartThings

SmartThings has what they call “Virtual Devices“, which is a way to define a custom device in terms of its capabilities. A Virtual Device can, for example, declare that it’s a switch (giving it an on/off toggle), a switch level (giving it a dimmer slider), or a “color control” (giving it a hue/saturation control). One can also insert code that’s called whenever one of the controls changes value. Perfect!

I created the following Virtual Device based on the Phillips Hue device template that interacts with the REST server mentioned previously.

All one needs to do at this point is create a new device within SmartThings that makes use of this Virtual Device.

Conclusion

This ended up working out way better than I expected. From what I can tell, it behaves exactly like a first-class citizen within SmartThings. I’m super happy with how easy (and fun!) it was.

Updates

  1. [Oct-21, 2015] — I noticed there was a bug in the SmartThings Virtual Device signature generation method. It wasn’t properly padding signatures beginning with 0. I fixed this by using the built in byte[].encodeHex().

Words With Friends Dictionary

I’ve been doing a bit of work with a move generator for Zynga’s popular variant on Scrabble, Words With Friends (WWF). To be any good at that, you need a list of words what WWF considers valid. Fortunately, they mention on their website that they use a slightly modified version of ENABLE, which is freely available.

Unfortunately, I noticed that sometimes WWF would complain about words I generated not being valid. After ensuring that the generated words were indeed in the ENABLE dictionary, it became obvious that Zynga’s dictionary removes some words from ENABLE. From experience, these generally included words that some people might consider offensive. For example, the word ABO, which is a pejorative for an Aboriginal Australian, is not recognized by WWF. In addition to this, ENABLE includes words that are longer than 15 letters. Since WWF’s board is 15×15, it’s impossible to form a word longer than 15 letters. Strangely enough, WWF still recognizes these words, despite them not being valid plays.

In an attempt to get a dictionary that’s as close to possible as the one WWF is actually using, I used an open source library I’ve been developing to ping them with all of the words shorter than 16 letters in the ENABLE dictionary. They removed about 50 words from the standard ENABLE dictionary.

A quick poll of a few “cheat” websites indicated that they were using the vanilla ENABLE dictionary, which includes the removed words.

You can find the updated dictionary here: wwf-dictionary.

Minification and disk caching

It’s probably pretty obvious that resource minification is a good thing. The bandwidth savings this affords you are probably negligible, but the fact that your site has fewer objects to load means faster page load times, and fewer requests sent to your server.

Ideally, you’ll have one file for all of your javascript source, and one more for all of your stylesheets. There are some pretty nifty WordPress plugins that do all of the work for you. This is great. Whenever you update your javascript or stylesheets, these plugins will generate a fresh minified file for you automatically. How lovely.

One has to be careful, though. Gathering up all of your source, minifying it, and throwing it all into a single file is a slightly costly operation. It’s a good thing, then, that all of the plugins I mentioned earlier provide some sort of caching mechanism. Some are a little less thoughtful of performance, however. Consider, for example, Better WordPress Minify. Here’s what the link to my minified javascript file looked like:

http://blog.christophermullins.net/wp-content/plugins/bwp-minify/min/?f=wp-includes/js/jquery/jquery.js,wp-content/plugins/crayon-syntax-highlighter/js/util.js,wp-content/plugins/crayon-syntax-highlighter/js/crayon.js,wp-content/plugins/crayon-syntax-highlighter/js/jquery.popup.js,wp-content/plugins/crayon-syntax-highlighter/js/fancybox/jquery.fancybox.pack.js,wp-content/plugins/crayon-syntax-highlighter/js/crayon_admin.js,wp-includes/js/thickbox/thickbox.js,wp-content/plugins/crayon-syntax-highlighter/util/tag-editor/crayon_te.js

This is clearly getting passed to PHP every time someone requests the page. Why is that bad? Because PHP is a resource hog. Serving a static file is almost always cheaper! This is especially true when PHP is just going to read a cached file from the disk anyway. As far as I can tell, that’s exactly what this plugin does. I can’t seem to find any way to change the caching mechanism without editing the source. Here’s how it serves cached files:

In contrast, here’s what the minified javascript link looks like under W3 Total Cache’s minify:

http://blog.christophermullins.net/wp-content/w3tc/min/92903ecb.6593de.js

This looks much nicer! This is, in fact, a static file. We could even serve it from a more lightweight web server, or push it to a CDN. W3TC supports doing both of these things. Here’s the magic: if the file exists, it’ll be served normally. If it hasn’t been generated, the request will eventually hit this rewrite rule:

rewrite ^/wp-content/w3tc/min/(.+\.(css|js))$ /wp-content/w3tc/min/index.php?file=$1 last;

This forwards the request to W3TC’s minify handler, which generates and caches the minified resource. From that point forward, it’ll be served as a static file. Nice!

Incidentally, W3TC’s page cache does exactly the same thing. When configured properly, this plugin makes it pretty easy to withstand pretty heavy traffic spikes.

You might not notice the difference in performance when your server is under a light load, but trust me. If you let PHP handle the caching, you’ll get a visit from the 502 Bad Gateway fairy in no time.

Of course, this all assumes you’re on a single host and have no interest in stuff like memcached.

LaTeX to Postscript Image

Earlier this week, I was preparing a presentation that was to include some simple equations I already had LaTeX for. The slideshow in question had far too many fiddly figures to justify bothering with beamer. Rather than fussing with powerpoint addins, I figured it’d be easiest to just include some images of the equations.

Rasterized images of text look horrendous in powerpoint presentations, so I figured I’d try to include postscript images instead. I found a nice post on the TeX section of stackexchange that detailed how to turn an equation into a PNG, and I adapted it for my own use.

Here’s a hacky bash script I wrote to make use of this technique. Note that if you want to do anything outside of math mode, you’ll have to remove the \[ \] surrounding \lformula. There were a few times I wanted to use \eqnarray and had to do this.