Comcast are scum

The ‘Digital Television Switchover’ took place here a couple of weeks ago. Not long after, we received this letter from Comcast[PDF 900kB]. While I suspect it doesn’t say anything that is actively illegal, I do think it could be highly misleading and is taking advantage of less well informed people1.


Q: How do I get my signal back?
A: There are several options, but the easy answer is to call Comcast.

How do you quantify ‘easy’? Comcast’s offer probably is ‘easy’ in the sense that once you request their service, they’ll send an installer over to set everything up for you. Of course RCN, Verizon, DirecTV, Dish Network or others would do the same thing, so it pays to shop around, if that’s what you want. They’ll all need to you stay at home to wait for the installer, then add some extra boxes to the outside of your house, drill holes and run cable around.

Assuming you have a TV antenna in place, I think it’s quite easy to buy a digital converter box, connect the antenna to it, and connect it to the TV.


Q: How do I get FREE Basic Cable?
A: It’s easy… sign up for Internet and/or phone (each starting at just $24.95/mo.)

So this is that special kind of ‘free’ where actually you have to pay for something. Whichever deal you choose you’re out a minimum of $10 every month to Comcast, and reading the small print you may also have to pay for equipment, installation, taxes and other fees. Don’t forget, it’s only ‘free’ for the first year. Do you expect to watch TV for longer than a year?

Compare this to a digital converter box. You can request a converter box coupon which gives you $40 off the price. The boxes are selling for around $40 – $60, so your maximum outlay will be about $20 (plus sales tax), with no ongoing cost. If you were previously watching broadcast TV, you can use the same antenna.

Should you take up Comcast’s offer?
I don’t intend to. I don’t think it gains you anything other than another monthly bill. If you just want to get broadcast TV back, a converter box is a cheaper option. If you really want cable, look around at all the possibilities.

1 Though my impression of US culture is that there is more of an expectation for people to inform themselves to avoid being taken advantage of.

dns323 podcast and torrent downloader

I’ve expanded on my previous podcast downloader program so that it gets torrents as well. I was running Automatic but it seemed silly to have two programs doing more or less the same thing.

I also didn’t like the configuration of Automatic, which has a global set of regexps it applies to all feeds to decide whether to download an item. My program has separate regexps for each feed1 which I think is a bit easier to deal with. In order to make the config file easy to parse, it is actually another Ruby file, which is read with instance_eval. By having methods defined as feed and matches the config file can just use those and look relatively sane:

feed 'http://some.feed.url/blah.rss'
matches 'title.to.match.*\d+'

(Some people would call this a DSL, but I think that’s stretching a point)

To make the torrents get into Transmission, the torrent downloader we’re using, I just call the transmission-remote command. Sometimes it’s easier to use what works.

So here’s the full program (see my previous post for how to set things up on the DNS-323):

#!/ffp/bin/env ruby
 
require 'open-uri'
require 'rss'
 
CONFFILE='/ffp/etc/mypodder2.conf'
PODCASTDIR='/mnt/HD_a2/podcasts'
TORRENTDIR='/mnt/HD_a2/torrents'
TORRENT_UPLOAD='/ffp/bin/transmission-remote "127.0.0.1:9091" -a "%s"'
 
class ConfigParser
	def initialize(feeds)
		@feeds = feeds
	end
 
	def parse(file)
		instance_eval(File.read(file), file)
	end
 
	def feed(url)
		@current = Feed.new(url)
		@feeds << @current
	end
 
	def matches(pattern)
		@current.add_pattern(pattern) if @current
	end
 
	alias :or :matches
 
end
 
class Feed
	def initialize(url)
		@url = url
		@patterns = []
	end
 
	def uri
		@url
	end
 
	def add_pattern(pattern)
		if(pattern && pattern.length > 0)
			@patterns << Regexp.new(pattern, true)
		end
	end
 
	def allow?(item)
		icompare = item.title
		@patterns.empty? || @patterns.find {|p| p =~ icompare}
	end
end
 
class Main
	def initialize(options = {})
		@options = {:config => CONFFILE, :verbose => false}.merge(options)
		@feeds = []
		read_config
	end
 
	def read_config
		ConfigParser.new(@feeds).parse(@options[:config])
	end
 
	def vprintln(sometext)
		if(@options[:verbose])
			puts sometext
		end
	end
 
	def torrent?(item)
		item.enclosure.type =~ /.*torrent/
	end
 
	def run()
@feeds.each do |feed|
    vprintln "checking feed #{feed.uri}"
    rss_content = ""
    open(feed.uri) do |f|
        rss_content = f.read
    end
    rss = RSS::Parser.parse(rss_content, false)
 
    rss.items.each do |item|
        vprintln " + checking item #{item.title}"
        if(item.enclosure && feed.allow?(item))
            iurl = item.enclosure.url
            vprintln " + + allowed item #{iurl}"
            ctitle = rss.channel.title.gsub(/\W/, '_')
		base = PODCASTDIR
		base = TORRENTDIR if torrent?(item)
            system("mkdir -p #{File.join(base, ctitle)}")
            system("chmod a+rw #{File.join(base, ctitle)}")
            ifile = File.join(base, ctitle, File.basename(iurl))
            unless File.exists?(ifile)
            	puts "About to download #{iurl} as #{ifile}"
            	system("wget -O #{ifile} #{iurl}")
 
		if(torrent?(item) && File.exists?(ifile))
			system(sprintf(TORRENT_UPLOAD, ifile))
		end
            else
            	vprintln " + + file exists"
            end
        end
    end                                
end
	end
end
 
if __FILE__ == $0
	require 'optparse'
 
	options = {}
 
	opts = OptionParser.new do |op|
		op.on("-f CONFIG", "--config CONFIG", "Specify config file (default #{CONFFILE})") do |f|
		      options[:config] = f
	        end
 
		op.on("-v", "--verbose", "Output messages") do
		      options[:verbose] = true
	        end
 
	        # No argument, shows at tail.  This will print an options summary.
	        op.on_tail("-h", "--help", "Show this message") do
	              puts opts
	              exit
	        end
 
	end
 
	opts.parse(ARGV)
	Main.new(options).run
end

1 At the moment, it ‘OR’s together multiple regexps on a single feed, but I’m thinking I may change that so it’s easier to exclude certain matches as well as include.

pulseaudio discoveries

  • Found out that PulseAudio includes built in support for creating multicast RTP streams of whatever audio you may be playing on your computer.
  • Then I found out if I turn on such a stream, it eats all the wireless bandwidth in the house and my wife complains her internet isn’t working. Nix that idea…

kill 1

I started wondering what would happen if I

kill -9 1

But I have enough of a guess that I don’t want to do it while my computer is doing anything else :-) Maybe later.

garden data

More garden-related geekery

We’ve started planting seeds in our vegetable beds, so naturally wanted to keep a record of what is where, and since when. Hence, Garden of Ethelred

It’s a fairly simple site – the data is in an xml file, and we have xsl transforms for the different pages. We chose xml because my wife is familiar enough with it to make changes too.

On the main hosting site I run a short PHP script which uses the XsltProcessor class. For testing on the home server the setup is more ‘fun’ – a CGI shell script which calls xsltproc. I’m not sure why I’m quite so amused at writing CGI as shell script, but I am.

screen grab logging

This is more of a short-term workaround than a solution, but it’s kind of fun.

I’m doing some performance testing of one of our java servers, and I want my test to run in the night while I’m asleep. The test has previously been run using jconsole to get graphs of memory and thread usage. This looks good but it’s a GUI only tool so not scriptable. I figured I could run it and get some screen captures.

import from ImageMagick is a tool for doing screen grabs from a script. However it needs an X11 window ID to grab a window, which can be a little tricky to find. Another problem I found when testing was that if I had another window overlapping the window I was grabbing, that part came out black, and when the screensaver kicked in, the scripted grab didn’t work.

Taking these problems, I came up with my workaround. First, run a vncserver, so that jconsole is in a separate display which isn’t affected by other windows or the screensaver. I also set the geometry to have just enough room for the windows I want.

vncserver -geometry 1000x1500 :1

Connect to this with a vncviewer, start up jconsole and set it up to display what I’m interested in.

Now I can start my test, and run my windowdump script:

#!/bin/bash
 
dumpdir=/home/edward/screencaps
wingrep=Monitoring
 
while true; do
    pdate=`date +%Y%m%d_%H%M`
    for winid in `xwininfo -display :1 -root -tree | grep $wingrep | tr -s ' ' | cut -d' ' -f2`; do
        import -display :1 -silent -frame -window $winid "${dumpdir}/${wingrep}_${winid}_${pdate}.png"
    done
    sleep 30m
done

This uses xwininfo to grab all windows in the display, then grep and cut to get the IDs of the ones I’m interested in, and pass those to import to grab them.

Quite silly really. If I have time I’ll be writing some code to just read numbers from JMX and log them to a file.

dns323 podcast downloader

Our server box is taking over lots of little download tasks for us. The next thing I wanted it to do is automatically get podcasts. Being a Ruby fan I knocked up a little script – it’s handy having RSS reading as a core language library!

#!/ffp/bin/env ruby
require 'open-uri'
require 'rss'
 
CONFFILE='/ffp/etc/mypodder.conf'
PODCASTDIR='/mnt/HD_a2/podcasts'
 
IO.readlines(CONFFILE).select{|l| l.strip.size > 0}.each do |feed|
    rss_content = ""
    open(feed) do |f|
        rss_content = f.read
    end
    rss = RSS::Parser.parse(rss_content, false)
 
    rss.items.each do |item|
        if(item.enclosure)
            iurl = item.enclosure.url
            ctitle = rss.channel.title.gsub(/\W/, '_')
            system("mkdir -p #{File.join(PODCASTDIR, ctitle)}")
            system("chmod a+rw #{File.join(PODCASTDIR, ctitle)}")
            ifile = File.join(PODCASTDIR, ctitle, File.basename(iurl))
            unless File.exists?(ifile)
            	puts "About to download #{iurl} as #{ifile}"
            	system("wget -O #{ifile} #{iurl}")
            end
        end
    end                                
end

All very well, but the server didn’t have Ruby on it. I saw there was a Ruby package in Optware, so I followed the instructions to install Optware and installed the Ruby package.

On testing Ruby I got a message like this:
“can’t load library ‘librt.so.0’”

Fortunately some other people on the forum had encountered this. To resolve it I downloaded their attached librt files and copied them to the /ffp/lib directory.

The next step was to set up a cron job to run the download script. Due to how the dns323 is set up, the recommended way to do this is a startup script which edits the crontab when it runs. This backup script has an example

Unfortunately after doing that, I found my script wasn’t running. It took a few experiments before I realized cron wasn’t setting up all of the PATH I needed. So I wrote a ‘cronhelper’ script to set the PATH and wrap up logging for my cron jobs:

#!/bin/sh
PATH=/ffp/sbin:/ffp/bin:/usr/sbin:/sbin:/usr/bin:/bin:/opt/bin
 
export PATH
CRONLOG=/mnt/HD_a2/ffp/home/root/cronlog
 
mydate=`date`
echo >> $CRONLOG
echo "*** $mydate" >> $CRONLOG
$* >> $CRONLOG 2>&1

That seemed to do the trick.

Wordpress misbehaving

This is a test post since I am having some trouble with Wordpress.

It’s probably about time I upgraded it, but I don’t like to upgrade software which works. But if it stops working… why would it stop working?

Gardening by Numbers

AKA what else can we turn into an engineering problem?

Last summer my wife and I bought a house, and for the first time we have our own garden. We were keen to grow some veg, but since we moved in the summer we had missed most of the growing season last year.

The garden isn’t enormous so we wanted to make efficient use of space. Looking for tips on the web, we found out about Square Foot Gardening which is a gardening method designed by an engineer! The name comes from the idea that you divide up growing space into square feet and plant a single kind of plant in each square. But the other important idea is that you build raised planting beds and fill them with a rich growing medium, rather than just planting in the ground.

So in the autumn I built our beds. Two of 8’ by 4’, about a foot deep, built with standard construction lumber. We lined the bottoms with chicken wire to try and prevent burrowing creatures from getting in. We filled the boxes with a 3:1 mixture of compost and vermiculite – this required over a ton of compost which was delivered to our driveway and we carried in buckets to the beds.

With the beds in place, we started planning what to plant in them. We made a list of what we wanted to grow, and then thought about how they could be arranged in the squares in the boxes. There are various constraints: for example, peas and beans have to go in the back row of squares, because only those will have climbing support. Squashes have to go in squares at the side of the box so they can droop outside. Shorter plants should go nearer the front so they aren’t in the shade of taller plants.

This started to sound like an optimization problem, and when I see those, I think ‘genetic algorithm’. So I knocked up a quick program (in Ruby of course) to help figure out the best placement for our vegetables, using the above rules and more. It generates random placements and scores them based on the rules, and can ‘mutate’ them by moving around plants.

Interestingly enough, the program would only get bad scores to start with, and we wondered if there was a problem with it. After some checking I found we had told it 12 squares worth of climbing plants, and we only have 8 squares where they can go. The program was fine once we changed the seasons on those so they could fit.

Now we are just waiting for winter to end so we can get planting.

gaming website relaunch

As I mentioned a while back I’ve been working on a rewrite of my gaming website. I decided last weekend that it was now good enough to switch over. So I present the updated Ordo Acerbus

As far as I’m concerned the nice feature of my new setup is that I edit pages using ssh and vi instead of in a text box in a browser. (And I kept forgetting my password).

I also discovered Facelift Image Replacement which I’ve used for the headers. I’m impressed – it was pretty easy to set up and it performs well.