Xserve is long dead.

Mac OS X Server is an app, not a standalone product anymore, and is a shadow of its former self.

So it would not surprise me if this announcement is the first step towards a partnership which could supply both iOS and Mac OS-friendly “big iron” server technologies for enterprise, an area which Apple clearly has no interest in pursuing.

Apple WWDC 2014: 9+1=Yosemite

|

And with one fell swoop, we know that Mac OS X 10.9’s successor is not Mac OS X 10.something, but is Mac OS X Yosemite.

(Or so I think. Other websites are reporting it as 10.10, though I haven’t heard that mentioned yet.)

OK, I’m wrong. 10.10 is mentioned in this press release, in a footnote.

A few people have asked why a tax increase of 2.849% is required for a budget increase of 2.59%, saying that it just doesn’t make sense. On the surface of the problem, it certainly doesn’t! Here’s a quick explanation which I hope helps make sense of these two different numbers.

For purposes of discussion, let’s assume that our town budget was $100 last year, and that we need $105 this year. That’s a 5% increase in the budget. If we had to raise all of that money ourselves through taxes, the tax increase would be 5%, too.

But some of that $100 comes from the state government. Let’s say the state contributed $50 of that budget last year, leaving the town to raise only $50. Now let’s say that the state will contribute the same amount this year, $50, leaving us to raise $55. Last year, we only had to raise $50. This year, we have to raise $55. Year over year, that’s a 10% increase in taxes.

So a 10% tax increase is required even though the budget only went up 5%, and that shows how the two numbers can be different.

In real life, we have the same thing going on, but with different numbers. This year’s budget is about $53 million, an increase of 2.57% over last year. About $13 million of our budget comes from the state, so last year we needed $39 million in taxes. This year, the state is giving us the same amount, about $13 million, so we’re left to raise just over $40 million in taxes. The difference between last year, ≈$39 million, and this year, ≈$40 million, is about 3.28%.

If the grand list were kept just as it is, the tax rate would be going up by 3.28%. But there are adjustments to the grand list year-over-year, too, and these adjustments reduce the amount to 2.849%, which is different from the budget increase of 2.57%.

No mystery, just math.

Want to know my position on this budget? I support it. Read why here.

(All of these numbers can be found in the Town’s 2014-2015 budget, which can be viewed here.)

Last week, the Tolland Town Council approved our 2014-2015 budget by a vote of 6-1. Now it’s up to Tolland’s voters to decide if it’s the right budget for us. As I said that night, I am suitably impressed with the process used this year to arrive at our budget, and I am fully in support of it.

So… why? After all, it contains a 2.849% increase in taxes, and any increase in taxes is bad. Even I have to agree with that.

But the big, hairy problem is that all costs are rising. I’m sure you’ve felt the squeeze at the pumps and in the checkout lines, with each paycheck and its increased insurance premiums—in just about every facet of life, you’re paying more for the same. (Or even for less.)

The town has felt the squeeze, too, and since the crash of 2008, previous Town Councils have been doing their best to keep tax increases to the absolute, barest of minima, in the hopes that things would get better. While the average tax increase over the past five years has been only about 0.5% per year, consumer prices have been increasing by 1.6% per year. You can see where this might be a problem.

In order to keep costs low, or at least constant, past councils have done some superb work in controlling costs. But they’ve had to delay maintenances, cut town staff (by nearly 10%!) and basically bet that the economy would recover in a meaningful way sooner than later. When, or if, the economy made its recovery, the Town could catch up on these things.

Unfortunately, six years later, while the rest of the nation seems to be in a minor recovery at best, Connecticut isn’t making much, if any, progress at all, and these delays cannot be tolerated anymore. We have no choice at this point but to respond to the needs of the schools and the town. New mandates from the state and federal governments are squeezing us even more, there are few savings left to be found, and things are beginning to crack—if they’re not broken already.

Simply: we have no choice but to increase the budgets of both the town and the school system.

In order to increase these budgets, income must increase, and income and outflows must balance. In a normal economy, property values increase, tax revenue increases, and there’s enough income to cover the increasing expenses of the town and school system. This year? Not so much. Our property values are flat, there have been few increases in the grand list, and yet prices rise all the same. We’re in between the proverbial rock and hard place, and the result is a requirement to increase taxes.

So, if a tax increase is necessary, how much do we increase them by? Do we increase them the smallest amount possible, straining every resource in town to do more with less, possibly breaking things which are expensive to fix later on? Do we increase them substantially so that everything which is stretched gets put back on an even keel? Or do we increase them somewhere in between, looking for a reasonable increase which addresses some of the needs and gets us back on a course to health?

It’s this last alternative that the Board of Education, the Town Manager, and indeed the Town Council have chosen, for better or worse, because there is a lot that needs to be done in town.

Our equipment isn’t going to fix itself, the salt we consumed during these last two harsh winters won’t restock itself, the departments which are stretched to the max will not be able to sustain this pace, and fuel won’t magically appear to fill the tanks of the trucks and cars this town needs to continue operations. Our schools won’t meet the curricular needs of our children, their safety and welfare will not be increased in any meaningful way, and we will not provide for the activities and support these children need to get the best start in life that we can provide for them as a town. None of this will happen without this budget and the tax increase it comes with.

And that’s why we… no, that’s why I voted in support of this budget.

On particular matters within the budget, there really were only two contentious issues, and that’s an amazingly low number and is a breath of fresh air to those who have been involved in town government for the past decade.

The first contentious issue is all-day kindergarten, and the superintendent found a way to fund that within the budget presented to the Town Council. For those who are angry, as I was, that he found this money after the budget was already submitted and feel that the budget should have been lowered to the promised “level services” level, I remind you that the Town Council asks our town manager to do the same thing, year after year: find us a way to fund X, even though it’s not in the already-approved budget. On the Superintendent’s priorities of all-day kindergarten and reducing the pay-to-play costs, I can’t comment—it’s not my area of expertise, and efforts to enlighten me by both sides of the argument have proven that the old adage about statistics holds true.

The second contentious issue is funding a School Resource Officer (SRO). I’m very much in support of this expense. As to its necessity, I can only point to the anecdotal evidence offered by the Town Manager and social services staff who say that it’s a good and necessary program. It is also my understanding—and I may be wrong—that the Superintendent’s priorities have included an SRO for many years and that, though there may be higher priorities on each of the school principals’ lists, an SRO has been quite a high priority for the principals in years past, too. Here, I also trust that when the Town Manager and the Superintendent, not necessarily two people who agree on much, agree on a priority and a way to fund it, it’s better than a good idea. Finally, we have an opportunity to bring a resource to our community which, if we do not do so now, we will not be able to do so for several years. The State Police, which is in my opinion the most cost-effective and reasonable way to have SRO services for Tolland, face a manpower shortage and we’d be on the bottom of the list for an SRO when we do decide we need one. On the other hand, if the SRO doesn’t work out to be as valuable as indicated, the position can be cut. We have very little to lose here.

Could the SRO money be spent on technology which, as I’ve heard firsthand from my kids, is ancient (at best)? Yes, it could be. But I also understand that there is a plan to upgrade it in an orderly fashion. Given that the curriculum is in an incredible state of flux right now, I can’t see that dumping money into technology without aligning the purchase with the curriculum change makes much sense. It would solve a problem right now, certainly, but I’m more in favor of solving a problem long term. We’ve had too many years of short-term thinking in this town driven by short election cycles, and we cannot afford short-sighted thinking anymore. There’s a plan for technology in place, and though it will need to be changed, derailing it is not my idea of wise spending.

So there you have my thoughts on this year’s budget and why I support it. Though I know it’s not perfect, I think this budget is exceptional not only for the process by which it was created, with development of consensus early in the process, but also because it represents an excellent mix of restraint and utility. Should you vote in support of it on May 6th? I think you should.

Snow Mastiff

|

Two winters ago, but still relevant today.

output_10a081.gif

The West Is Dead. Long Live the West. | Blog & Mablog

|

I just started reading Douglas Wilson’s blog courtesy of a referral from Concert F’s Roger Keane. And, boy! am I glad I have. Thanks, Roger!

To those of you who, as Mr. Wilson puts it, are looking at 2014 balefully, I recommend reading his entry of a few days ago, The West Is Dead. Long Live the West. He quotes The Everlasting Man (G.K. Chesterton, 1925) which forms a substantial portion of his thesis:

“Christendom has had a series of revolutions and in each one of them Christianity has died. Christianity has died many times and risen again; for it had a God who knew the way out of the grave.”

Alone, this is enough for hope in the future. With the added encouragement of Mr. Wilson, I wish you a happy new year!

Christmas is Hard Work

|

Seriously, Christmas is hard work. After we unwrapped presents…

2013-12-25 09.25.44 copy.jpg

…we napped.

2013-12-25 15.19.38.jpg

The Nintendo Prayer

|

A friend posted pictures of his sons playing with their Nintendos at their dining room table. It looked like they were in prayer, so I hacked this together:

Our Nintendo, which art on the table,
Hallowed by thy games.
My high score is won, my brother’s be none,
On Mario Kart as it is on Super Mario.
Give us this day our daily cheat code,
Up up down down left right left right B A,
As we forgive those who use it against us.
And lead us not into Bowser’s Castle,
But deliver stuff from Amazon.
For mine is the Mario, the power up and the Luigi,
For Yoshi and Birdo,
Amen

Next up: “Hail Mario, full of grease…”

It’s about time that the media shed the pretense of being “unbiased” and came clean right up there in the masthead.

“NEW YORK TIMES—Today’s Edition: 89% liberal”

“FOX NEWS: Trying to be 50/50, but looking a little imbalanced these days”

“WASHINGTON POST: We’re owned by a big-time Obama contributor. What do you think you’re going to get?”

“MSNBC: We’re not even trying.”

“CNN: Wait, we’re still relevant?”

I never knew Google’s “that little guy” was called Pegman until today, when I also noticed that he is attired for the season.

Screen Shot 2013-12-18 at 2.02.47 PM.png

Is that an Internet in your pocket, or are you just happy to see me?

Two iOS 7 Observations

|

I’m sort of surprised nobody has made these observations (that I can find, anyway).

First, did you notice that all of the so-called “flat” interface elements are ridiculously easy to draw with vector graphics? As I’ve stated before, the various flavors of Apple OS are going to be resolution independent someday. The departure from textures and bitmapped elements will make this transition much easier, and the sweeping hands on the iOS Clock app icon is a pretty good example of a traditionally-bitmapped element which has become all or nearly all vector graphics.

Second, when you’re composing an E-mail, Mail app on iOS 7 does a pretty good job at guessing which account the message should be sent from. Let’s say you have two E-mail accounts, “joe@eccles.net” and “joe@company.com” and that your default “from” address is “joe@eccles.net”. When you start writing a message, the “from” field will show “joe@eccles.net”, as it should. But then when you put in a recipient, such as “sally@company.com”, iOS 7 will change the “from” account to the “company.com” address. This feature would have saved me and my co-workers much confusion as I have been known to send them E-mails from my personal account when I meant to send them from my work account.

That’s all for now.

The animation in the iOS 7 Clock app is gorgeous, and it addresses my long-standing gripe about the minute hand motion. Older versions of clocks would move the minute hand once per minute in a herky-jerky motion which didn’t match the beautifully designed motion of the second hand. All of them sweep in the live Clock icon, too!!

The iOS 7 version moves the minute and hour hands right along with the second hand in the smoothest, most incredibly-lickable animation I’ve longed for.

It’s the little things, and they got this one right… finally.

[Ironic—or coincidental—that it took a lessening of the realism of the clock face itself to get the realism of the clock mechanicals right, isn’t it?]

Why the 5c? I Think I Know...

|

Some blogs are asking, “Why is Apple making the iPhone 5c? Who’s the target audience?”

Remember this headline from a year ago?

Foxconn Exec Says iPhone 5 Is the ‘Most Difficult Device’ It Ever Made

And remember this headline from the not-too-distant past?

iPhone sales up, but Apple profit margin falls

Still wondering why the 5c exists and the 5 has been canned? If so, I’ll connect the dots for you.

How many assembly workers did you see in the manufacturing video for the 5c? Hint: None. That’s because robots—which don’t require food, rest, or suicide prevention nets—are doing more of the 5c assembly process than was possible for the 5. They’re faster, they’re more precise, and they make the 5c much less expensive to manufacture than the 5.

The net result? Higher margins on what is, essentially, an identical product to the outgoing 5. The colorful cases and shells are just the excuse to make the new product.

So, let’s answer the first question: who is Apple targeting with the 5c?

Investors.

[As of noon today, Apple stock is down 5% meaning that investors don’t feel all that targeted.]

Hi, Mac user. Do you work in a Windows environment and get links from your colleagues in documents, mail messages, etc.? Here’s a convenient tool which helps with that problem.

It’s called WinShortcutter and it’s free.

Netflix Suddenly Has Profiles

|

Cool!

They just showed up on the AppleTV!

I just bought Keyboard Maestro from Stairways Software last week and already it is de-frustrating my co-existance with Microsoft Outlook in meaningful ways. Here are two ways:

Intercept Undesired “Close” Keystrokes

Because I use a two-monitor setup with Outlook over there on that far-off monitor (actually, my MacBook display), I oftentimes switch to Outlook to read a message but forget to switch focus to Safari (or another app) where I might hit ⌘W… and inadvertently close an Outlook “Main Window” (as they’re called).

I instituted the following KM macro to prevent this problem: Screen Shot 2013-07-29 at 12.25.55 PM.png

It merely looks to see if the current frontmost window contains “Calendar” or the name of my company (Bloomy Controls, Inc.) which shows up in the message viewer window no matter which mailbox is selected, e.g. “Inbox • Bloomy”. This is true except for Smart Folders, which I don’t use. If the frontmost window doesn’t meet these conditions, it sends a ⌘W to Outlook and that window will close.

Sensible Find Shortcuts

I hate Microsoft Outlook’s Find functionality. Other than the fact that it can, indeed, find anything in any folder, the interface to use it is awful. And since I use the Deleted Items folder as my brain, the Find function is very important. Using it out of the box involves pressing ⇧⌘F and then clicking on the “All Items” to un-limit the search scope (every time—why, Microsoft, doesn’t it remember what scope I used before?).

Getting away from Find involves clicking on a red X in a circle called “Close”… unless you actually selected a message to look at, in which case the ribbon (WHICH I HATE) changes state to “Home”. If you did (and why wouldn’t you?), you have to click on the Search portion of the Ribbon selector and then on the red X in a circle.

Keyboard Maestro macro to the rescue!

Screen Shot 2013-07-29 at 12.34.34 PM.png

This one’s a bit more complicated as it has to check to see what state the Outlook window is in. But pressing ⇧⌘F becomes the equivalent of “Search all items,” and pressing it again closes out the search.

(By the way, this one’s not bulletproof. Among other things, if there’s an E-mail window open with the word “Bloomy” in the title—substitute your own text here, of course—it’ll get brought to the front and might cause the rest of the macro to fail.)

I’ll add more macros as I make them.

If you stumbled across this blog looking for information on the WS-2010-13 PC interface, then, man! are you in luck!

A previous entry here shows the code I use with an mbed processor to do the posting to a database on a webserver. If you have a Linux or Windows box, however, the code below might do you a bit of good. If not, then… well, keep hacking!

(Note: I just discovered that Feedbin mangles the code a bit. If you’re getting the code via an RSS reader, you might not be seeing the code in its entirety. Visit the source article for an unadulterated copy of the code.)


#!/usr/bin/perl -w
#


# WeatherStationInterface.cpp
# An interface for the LaCrosse Technology WS-2010-13 PC Interface.

# Copyright (C)2012 William N. Eccles

# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be includied in all copies or substiantial portions of the
# Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECITON WITH
# THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


use Win32::SerialPort;
use LWP::UserAgent;

# found these items somehwere--maybe it was from the original LCT docs, but I'm not sure
#define SOH 0x01
#define STX 0x02
#define ETX 0x03
#define EOT 0x04
#define ENQ 0x05
#define ACK 0x06
#define DLE 0x10
#define DC2 0x12
#define DC3 0x13
#define NAK 0x15

# command:     
# @commands = (
#   "\x01\x30\xcf\x04", # '0' = Poll DCF time 
#   "\x01\x31\xce\x04", # '1' = Request dataset 
#   "\x01\x32\xcd\x04", # '2' = Select next dataset 
#   "\x01\x33\xcc\x04", # '3' = Activate 9 temperature sensors 
#   "\x01\x34\xcb\x04", # '4' = Activate 16 temperature sensors 
#   "\x01\x35\xca\x04", # '5' = Request status 
#   "\x01\x36\x53\xc9\x04"  # '6' = Set interval time 
# );

# some utility routines

# return the high nybble of a byte in the low nybble

sub H {
    my( $byte )= $_[0];
    return $byte>>4;
}

# retun an usigned high nybble of a byte in the low nybble
sub HS {
    my( $byte )= $_[0];
    return ($byte >> 4) & 0x07;
}

# return the sign of a byte
sub S {
    my( $byte )= $_[0];
    $byte = ($byte & 0x0F) >> 3;
    return (($byte==1) ? -1 : 1);
}

# return the low nybble of a byte
sub L {
    my( $byte )= $_[0];
    $byte = ($byte & 0x0F);
    return $byte;
}

# return an unsigned low nybble of a byte
sub LS {
    my( $byte ) = $_[0];
    $byte = ($byte & 0x07);
    return $byte;
}

# make the WS2010 sleep. Show a debug message while we're at it.
sub gotosleep {

    print "telling WS2010 to sleep.\n";
    $ob->dtr_active(F);
    return;
}

# wake up the WS2010. Print lots of debugging stuff while doing it.
sub wakeup {

    # make a low->high transition on DTR to let WS2010 know we're here.

    $ob->rts_active(No);
    $ob->dtr_active(F);
    $ob->dtr_active(T);

    ($count, $result) = $ob->read(2);

    $count = length($result);
# debug
    print "Wokeup WS2010 and received ",$count," characters.\n";        
    print join(" ",unpack("C*",$result)),"\n";
# gubed

    @return = unpack("C*",$result);

# debug
    if (@return!=1) {
        warn "Invalid number of characters received on wakeup.\n";
    }

    if ($return[0]!=3) {
        warn "WS2010 returned invalid wakeup signal. \n";
    } else {
        print "WS2010 is ready.\n";
    }
# gubed
}


#
# main program
#

print "Opening serial port...\n";

# for now, we'll assume that some other program (such as WS-2010 PC) has been
# used to set the polling interval and number of sensors. We'll write one
# that will be used someday.
# The problem is that if you do this every time this program is run, it
# clears the history stored in the WS2010. I think it's just a matter of
# sending command #3 (or #4) and #6. Come to think of it, I don't think
# I've ever recorded and examined this transaction.  

# open the com port
$ob = Win32::SerialPort->new ('COM1');

die "Can't open serial port COM1: $^E\n" unless ($ob);

# setup the serial port. Die with meaningless message if we don't succeed.  
$ob->baudrate(9600) || die "Failed to set the baudrate. Dunno' why.\n";
$ob->parity("even") || die "Failed to set the parity. Dunno' why.\n";
$ob->databits(8) || die "Failed to set the databits. Dunno' why.\n";
$ob->stopbits(2) || die "Failed to set the stopbits. Dunno' why.\n";
$ob->handshake("none") || die "Failed to set the handshake.\n"; 
$ob->write_settings || die "Failed to write settings to port. Dunno' why.\n";

# debug
    my($baudrate) = $ob->baudrate;
    my($parity) = $ob->parity;
    my($databits) = $ob->databits;
    my($stopbits) = $ob->stopbits;
    print "$ob opened at $baudrate/$databits/$parity/$stopbits\n";
# gubed

$ob->read_interval(100);
$ob->read_const_time(5000);

#
# the big loop
# each time through the loop:
#   wait six minutes (unless it's the first time through this loop)
#   open the com port
#   read data and post it until it's all gone
#   close the com port
#
# There's enough debugging stuff in here that I won't bother with labeling it.
# Suffice it to say, it's all the "print" stuff, none of which is required for
# proper operation of the system.

$first = 1;

while (1) {

    if (!$first) {
        print "Sleeping for a long time...\n";
        gotosleep();
        $i=10;
        while ($i>=1) {
            print $i,"...\n";
            sleep(120);
            $i=$i-1;
        }
        print "0.\n";
        sleep(1);

    }
    $first = 0;

    $haveData = 1;


    while ($haveData) {

        # request a data block

        wakeup();

        print "Requesting data.\n";

        $ob->write("\x01\x31\xce\x04");

        ($count, $result) = $ob->read(100);

        $count = length($result);

        print $count," characters returned:\n";

        print join(" ",unpack("C*",$result)),"\n";

        @return = unpack("C*",$result);


        if ((@return==0)||($return[0]!=2)||($return[@return-1]!=3)) {
            warn "Bad dataset received or no data found.\n";
            $ob->purge_rx;
            $haveData = 0; # quit asking for data--give a cooloff time
        } elsif (($return[1]==1)&($return[2]==16)) {
            print "No data available.\n";
            $haveData = 0;
        } else {
            # select the next data block
            print "Requesting next block.\n";
            $ob->write("\x01\x32\xcd\x04");
            ($count, $newresult) = $ob->read(5);
            print $count," characters returned:\n";
            print join(" ",unpack("C*",$newresult)),"\n";
            @newreturn = unpack("C*",$newresult);
            if (($newreturn[2]==16)&($newreturn[1]==1)) {
                print "No dataset ready.\n";
                $haveData = 0; # no data to be had
            }


            # strip out the ENQ escape sequences and leading STX and length, trailing ETX
            @stripped = ();
            for ($i=2; $i<@return-2; $i++) {    
                if ($return[$i]==0x05) {
                    @stripped = (@stripped, $return[$i+1]-0x10);
                    $i++;
                } else {
                    @stripped = (@stripped, $return[$i]);
                }
            }
            print "Dataset read.\n";
            @return = @stripped;
            print join(" ",@return),"\n";


            $db = $return[1]*256+$return[0];
            $dt = $return[3]*256+$return[2];
            ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time-($dt*60));
            $year=1900+$year;
            $mon++;

            printf "dataset %06d %06d %04d/%02d/%02d:%02d%02d\n",$db,$dt,$year,$mon,$mday,$hour,$min;
            $getstr=sprintf("http://example.com/yourQuery.php?loc=12345&dataset=%06d&datetime=%04d-%02d-%02d%%20%02d:%02d:00",$db,$year,$mon,$mday,$hour,$min);

            ($i, $i, $i, @return) = @return;
            # I know, there's probably a better way to do it... strip off header info.

# each reading consists of the reading itself and whether or not it's a new or old value.
# variable names are somewhat consistent with this.

            $t1c = (LS($return[2])*10+H($return[1])+L($return[1])/10)*S(L($return[2]));
            $t1f = 9/5*$t1c+32;
            $h1 = LS($return[3])*16+H($return[2]);
            $n1 = (S(L($return[3]))==-1) ? "NEW" : "OLD";

            if ($t1f>70) {
                $hif = -42.379+2.04901523*$t1f+10.14333127*$h1-0.22475541*$t1f*$h1-
                    ((6.83783e-3)*$t1f*$t1f)-((5.481717e-2)*$h1*$h1)+
                    ((1.22874e-3)*$t1f*$t1f*$h1)+((8.5282e-4)*$t1f*$h1*$h1)-
                    ((1.99e-6)*$t1f*$t1f*$h1*$h1);
                $hic = ($hif-32)*5/9;
            } else {
                $hif = $t1f;
                $hic = $t1c;
            } 
            $getstr .= "&t1=$t1f&h1=$h1&n1=$n1&hif=$hif&hic=$hic";

        #   print "S1: $t1f∫F $h1% $n1\n";
            print "HI: $hif∫F $hic∫C\n"; 

            $tic = (LS($return[29])*10+H($return[28])+L($return[28])/10)*S(L($return[29]));
            $tif = 9/5*$tic+32;
            $hi = LS($return[30])*16+H($return[29]);
            $ni = (S(L($return[30]))==-1) ? "NEW" : "OLD";

            $getstr .= "&ti=$tif&hi=$hi&ni=$ni";

        #   print "IN: $tif∫F $hi% $ni\n";

            $sp = (HS($return[24])*100+L($return[24])*10+H($return[23])+L($return[23])/10)*.6215;
            $dir = (L($return[26])%4)*100+H($return[25])*10+L($return[25]);
            $spreadraw = int(L($return[26])/4);
            if ($spreadraw==1) {
                $spread = 22.5;
            } elsif ($spreadraw==2) {
                $spread = 45;
            } elsif ($spreadraw==3) {
                $spread = 67.5;
            } else {
                $spread = 0;
            }
            $nwin = (S(H($return[24]))==-1) ? "NEW" : "OLD";

            $getstr .= "&sp=$sp&dir=$dir&spread=$spread&nwin=$nwin";

        #   print "WIN: $sp mph at $dir +/- $spread $nwin\n";

            if (($t1f<=50)&($sp>3)) {
                $wc = 35.74+(0.6215*$t1f)-(35.75*($sp**0.16))+(0.4275*$t1f*($sp**0.16));
            } else {
                $wc = $t1f;
            }

            $getstr .= "&wc=$wc";

            $pr = (H($return[27])*100+L($return[27])*10+H($return[26])+200)/33.775;

            $getstr .= "&pr=$pr";

        #   print "PR: $pr hPa\n";
        #   0.0145636 in/count

# rain is a tricky thing. The rain sensor merely counts tips of the seesaw and reports
# that number of counts periodically. It's a 12 bit counter, so there's a challenge
# in the database/reporting program to discover that it's rolled over and has not
# reset through a battery change or fluke. What I've done is determined that
# if the count goes from "high" to "low", where high is currently set to >4080, and
# low is <20, then there was, indeed, a rollover and not a reset. I also store both
# the raw value reported by the rain gage as well as the delta from the last reading
# so that a simple SUM query can be done to get the total rainfall during a given
# period of time.

            $rn = (($return[22]&0x7F)*256+$return[21]);
            $nr = (S(H($return[22]))==-1) ? "NEW" : "OLD";

            $getstr .= "&rn=$rn&nr=$nr";



            $ua = new LWP::UserAgent;
            $ua->agent("EcclesAgent/0.1 ".$ua->agent);

            $httpError = 1;
            while ($httpError) {
                print $getstr,"\n";
                my $req = new HTTP::Request GET => $getstr;
            #   $req->content_type('application/x-www-form-urlencoded');
            #   $req->content('match=www&errors=0');
                my $res = $ua->request($req);
                if ($res->is_success) {
                    print $res->content;
                    $httpError = 0;
                } else {
                    print "HTTP Error\nWaiting to try again...\n";
                    $i=10;
                    while ($i>=1) {
                        print $i,"...\n";
                        sleep(1);
                        $i=$i-1;
                    }
                    print "0.\n";
                    sleep(1);
                }
            }
            undef $req;
            undef $ua;
#

        gotosleep();
sleep(1);
        } # end of massive if statement

#       print "Requesting status.\n";

#       wakeup();
#       $ob->write("\x01\x35\xca\x04");

#       ($count, $result) = $ob->read(100);

#       $count = length($result);

#       print $count," characters returned:\n";

#       print join(" ",unpack("C*",$result)),"\n";


    } # end of while ($havedata)    


} # end of while (1)

undef $ob; # close serial port

When I searched for Pet Supplies Plus in Manchester, CT, I got this as part of Google’s search results:

Screen Shot 2013-06-28 at 5.28.31 PM.png

They’re cheap, but I’ve never seen a menu…