Monitoring the Dog Bowl with Home Assistant

We have two dogs and three cats. Long ago we gave up on the idea of using a normal sized water bowl and switched to a large salad bowl. This wasn’t ideal for a variety of reasons, including aesthetics, so we recently swapped it out for a decorative fountain in the corner of our kitchen.

This has been going great and with it’s 7 gallon capacity has let us go as long as a week between refills. The downside is it’s easier to forget to refill it when it doesn’t need to be done as often. Coupled with most of the water being stored in the base, you can’t tell it’s getting low at a glance as you walk by.

Technology to the rescue!

I installed a liquid level sensor manufactured by Milone Technologies purchased from Adafruit. If you go this route, note that the enclosed models aren’t just the eTape in a tube, they include a resistance to variable voltage board with a small MCU operating at 5V. I’m sampling this from an Adafruit HUZZAH32 , an ESP32 based board running at 3.3 V. According to the data-sheet, a 3.3 voltage from the 12″ eTape is around 9″. In my case, this would be overflowing so I can read the voltage directly, if that doesn’t work in your situation, you’ll need a voltage divider to bring the max value below 3.3. The data-sheet also says the sensor needs a minimum of 6 volts to run but I haven’t had any trouble running it from the 5 V USB pin on the HUZZAH32.

My source code for the ESP32 is on Github but I’ve reproduced the important bit here:

int currentTotal = 0;
for (int i = 0; i < numReadings; i++) {
  currentTotal = currentTotal + analogRead(level_pin);
  delay(1000);
}

total = total - readings[readIndex];
readings[readIndex] = currentTotal / numReadings;
total = total + readings[readIndex];
int average = total / numReadings;

int level = map(average, 0, 4095, 0, 1200); // convert to ~mm of liquid * 10
char msg[5];
snprintf(msg, 5, "%i", level);
mqtt_client.publish(topic, msg);

The top of the water in the base of the fountain is rather turbulent with constant water splashing down as it drains from the base so we do some averaging. We take a few readings one second apart and average those. We then average that reading with the readings over the last few minutes and fire it off to MQTT for use in Home Assistant.

Home Assistant Configuration

Home Assistant configuration is straight forward with the MQTT sensor component. I’ve added a template sensor to convert the raw sensor reading into a percentage for easier human digestion. config.yaml

sensor:
  - platform: mqtt
    state_topic: kitchen/fountain/level
    name: Fountain Water Level
    unit_of_measurement: mm
    force_update: true
    availability_topic: kitchen/fountain/available
    icon: mdi:fountain
  - platform: template
    sensors:
      fountain_fill:
        friendly_name: Fountain Fill
        entity_id: sensor.fountain_water_level
        unit_of_measurement: "%"
        value_template: "{{ ((float(states.sensor.fountain_water_level.state) - 895) / 305 * 100) | round(0) }}"
        icon_template: mdi:fountain

Of course the real win of Home Assistant is using automations.

automation:
  - alias: 'Fountain Water Level Evening'
    trigger:
      platform: time
      at: '18:29:00'
    condition:
      condition: numeric_state
      entity_id: sensor.fountain_water_level
      below: 900
    action:
      - service: script.notify_tts_all_t
        data:
          message: The fountain is low on water.

Our evening voice notifications come around 6:30 pm when we are likely to hear them. script.notify_tts_all_t sends a push notification to my wife and my phones, speak the notification on Google Home and Alexa speakers throughout the house, and post a tweet on my house’s twitter account. The source for that script as well as the rest of my Home Assistant config is available on Github.

Finding Stuff In Your Local CPAN Mirror

So you’ve got this great new local CPAN you can use to install stuff you know about but how do you find stuff you don’t know about? There are two main ways I inspect the contents of my local CPAN mirror. The first is a simple shell function for searching package names, the second has a bit more to it but we will save that for later.

grep to the Rescue

One of the metadata files in our local mirror isn/modules/02packages.details.txt.gz. Let’s take a look inside.

/modules/02packages.details.txt.gz

File:         02packages.details.txtnURL:          http://www.perl.com/CPAN/modules/02packages.details.txtnDescription:  Package names found in directory $CPAN/authors/id/nColumns:      package name, version, pathnIntended-For: Automated fetch routines, namespace documentation.nWritten-By:   CPAN::Mini::Inject 0.33nLine-Count:   145223nLast-Updated: Tue, 12 Aug 2014 15:01:40 GMTnnAAA::Demo                         undef  J/JW/JWACH/Apache-FastForward-1.1.tar.gznAAA::eBay                         undef  J/JW/JWACH/Apache-FastForward-1.1.tar.gznAAAA::Crypt::DH                    0.04  B/BI/BINGOS/AAAA-Crypt-DH-0.04.tar.gznAAAA::Mail::SpamAssassin          0.002  S/SC/SCHWIGON/AAAA-Mail-SpamAssassin-0.002.tar.gznAAAAAAAAA                          1.01  M/MS/MSCHWERN/AAAAAAAAA-1.01.tar.gznAAC::Pvoice                        0.91  J/JO/JOUKE/AAC-Pvoice-0.91.tar.gznAAC::Pvoice::Bitmap                1.12  J/JO/JOUKE/AAC-Pvoice-0.91.tar.gznAAC::Pvoice::Dialog                1.01  J/JO/JOUKE/AAC-Pvoice-0.91.tar.gzn...

nnn

The format is pretty straightforward. After a few header lines, we have onenline per package seen by the indexer. The lines consist of three whitespacenseparated columns of a package name, current package version (if the indexer found one),nand latest distribution containing it. The AAC-Provoice and Apache-FastForwardndistributions show us that each individual package within the distribution isnlisted. We could easily use zgrep to find packages that match a pattern.

nn

/modules/02packages.details.txt.gz
1n2n3n4n5n
$ zgrep -i tiny ~/Dropbox/minicpan/modules/02packages.details.txt.gznAcme::Has::Tiny                   0.001  T/TO/TOBYINK/Acme-Has-Tiny-0.001.tar.gznAcme::Module::Build::Tiny          0.0   D/DA/DAGOLDEN/Acme-Acme-Module-Build-Tiny-0.06.tar.gznAcme::Tiny                        0.003  E/ET/ETHER/Acme-Tiny-0.003.tar.gzn...

nnn

This is a little long to type regularly. A shell alias isn’t appropriate as wenwant to sandwich the provided input between zgrep and the path to the file.nFortunately, a shell function will allow us to do this without much morenverbosity.

nn

1n2n3n
function cpangrep {n    zgrep $* /home/yourusername/Dropbox/minicpan/modules/02packages.details.txt.gzn}n

nnn

Dropping this line in your .zshrc or .bashrc will allow you to searchn02packages.details.txt.gz with the cpangrep command.

nn

1n2n3n4n5n6n7n8n
$ cpangrep tiny | wc -ln21n$ cpangrep -i tiny | wc -ln344n$ cpangrep -i tinynAcme::Has::Tiny                   0.001  T/TO/TOBYINK/Acme-Has-Tiny-0.001.tar.gznAcme::Module::Build::Tiny          0.06  D/DA/DAGOLDEN/Acme-Acme-Module-Buildn...n

nnn

Since we use $* instead of $1, all arguments we give to the function arenpassed on to zgrep. This allows us to do things like search with or withoutnignoring case on demand.

nn

CPAN::Mini::Webserver

nn

CPAN::Mini::Webserver provides anscript that launches a web server providing an interface to your local minicpan.nUsage couldn’t be easier, install CPAN::Mini::Webserver then runnminicpan_webserver. By default the server binds to port 2963 but you cannchange this with the --port argument.

nn

If you’ve used search.cpan.org ornmetacpan, there should be no surprises in how to navigatenaround the site. Thendocumentation has information onna few things you can plug in to your .minicpanrc to enable things like fullntext indexing.

nn

Here are a few screenshots of CPAN::Mini::Webserver to wet your appetite.

nn

nnnn

n

Taking CPAN With You

nn

CPAN is THE killer feature for Perl. Onenproblem you may have from time to time due to it’s online nature is availabilitynissues. How do you install CPAN modules from a plane, train, or automobilenwithout wifi? Or from the middle of nowhere in Oregon (Ione, OR)? Or timely install Moose andnit’s dependencies from a dial-up connection in the middle of the desertednOklahoma plains (Leedey, OK).nWell, my fellow perler, I have been to these places forgotten by the Internets,nand I have installed CPAN modules with impunity.

nn

CPAN::Mini

nn

CPAN::Mini, driven by the includednminicpan,ncreates a local mirror of CPAN containing only the latest non-developer releasenof every distribution on CPAN. With CPAN::Mini yountoo can install CPAN modules with impunity from exotic locations. But just hownmuch disk space must you dedicate for such awesomeness? My mirror is currentlynjust under 3 GB.

nn

Start by installing CPAN::Mini with your CPAN client of choice:

nn

1n2n3n4n5n
$ cpanm CPAN::Minin$ # orn$ cpanp install CPAN::Minin$ # orn$ cpan CPAN::Minin

nnn

Now you need a config file in ~/.minicpan.

nn

~/.minicpanrc
1n2n3n4n
local:   ~/Dropbox/minicpannremote: http://cpan.hexten.net/nalso_mirror: indices/ls-lR.gznskip_perl: 1

nnn

These options should be fairly self-explanatory. Check out thenCPAN Mirrors site to find a healthy mirror in your regionnfor the second line. Now, just run minicpan and wait. Periodically,nyou’ll want to run this command again to download new dists and delete old ones.nThat’s all it takes to be the owner of a shiny tiny local CPAN mirror.

nn

Using Your Shiny New CPAN Mirror

nn

To use your new mirror, just specify the path to your CPAN::Mini mirror in yournCPAN client of choice.

nn

cpanm

nn

For cpanm Inhave an alias in my .zshrc:

nn

1n
minicpanm='cpanm --mirror ~/Dropbox/minicpan --mirror-only'n

nnn

The same line should work in a .bashrc as is. Then, to install a new CPANnmodule, just run minicpanm ACME::urmom.

nn

cpanp

nn

Run cpanp s edit and your editor will launch. If the file you are innisn’t empty, skip to the next step. Now run cpanp s save to copy thencurrent configuration to the user config file and then run cpanp s editnagain.

nn

Find the hosts config and add your new entry at the beginning.

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n
$conf->set_conf( hosts => [n      {n        'host' => '',n        'path' => '/Users/yourname/Dropbox/minicpan',n        'scheme' => 'file'n      }n      {n        'host' => 'ftp.cpan.org',n        'path' => '/pub/CPAN/',n        'scheme' => 'ftp'n      },n      {n        'host' => 'www.cpan.org',n        'path' => '/',n        'scheme' => 'http'n      },n# ...n

nnn

Save and exit your editor.

nn

cpan

nn

For cpan, you use the interactive shell to update the configuration:

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n18n19n
$ cpanncpan shell -- CPAN exploration and modules installation (v2.00)nEnter 'h' for help.nncpan[1]>  o conf urllist unshift file:///Users/yourname/Dropbox/minicpamnPlease use 'o conf commit' to make the config permanent!nnncpan[2]> o conf urllistn    urllistn    0 [file:///Users/yourname/Dropbox/minicpam]n    1 [http://cpan.erlbaum.net/]n    2 [http://cpan.mirror.clemson.edu/]n    3 [http://cpan.develooper.com/]nType 'o conf' to view all configuration itemsnnncpan[3]> o conf commitncommit: wrote '/Users/yourname/.local/share/.cpan/CPAN/MyConfig.pm'n

nnn

Enjoy (Faster) CPAN Installs From Anywhere

nn

That’s it! From here on out your disk speed and not your Internet connection,nor lack thereof, will be your throttle in your CPAN client of choice acquiringndistribution packages.

n

Listening to Two Icecast Streams at Once

I run a site with two scanner feeds for my local town, Gallowaynow.com, one for EMS & Fire and another less publicized one for PD. I regularly listen to both of these feeds when I’m not around the scanners supplying them. How do you listen to two feeds that may be talking at the same time and make sense of anything? With sox the Swiss Army knife of sound processing. By playing the two mono feeds out each channel of a stereo device, the brain can easily deferenciate between the two sources. This may not work very well from laptop speakers but works great from speakers with a few feet of seperation or headphones. sox can easily handle modifying the streams likes to work with files or piped streams so first we need to grab the icecast stream.

nn

fIcy

nn

fIcy is an open source suit of tools for grabbing icecast streams. It can do things like grab a stream and save it to mp3s when metadata changes, checkout the examples on its site for some of the other neat things it can do. Rather than invoke fIcy directly, I use fPls, included in the suite which can retry if the connection drops. Invocation is fairly straight forward:

nn

1n
fPls -T 10 -L-1  http://gallowaynow.com:8000/stream.m3u -t

nnn

The -T 10 waits ten seconds between reconnection attempts, -L-1 tells it to try indefenitly, next is the stream URL, and -t outputs the stream metadata to STDERR when it changes.

nn

sox

nn

sox is crazy flexible and can manipulate audio files in almost any way imaginable. See the examples in the man page or this blog post for just a sampling (heh, sampling) of the sort of things sox can do. In our case we want to take a mono mp3 on STDIN and place that audio in just one channel of a stereo stream and then play it. We use the remix effect to accomplish this. Since transforming and then playing the audio is common, sox includes a play binary that takes all the same commands sox does but then plays the output.

nn

1n
play -q -t mp3 - remix 1 0n

nnn

Remix takes a space separated list of input channels to be output on the positional output channel. In this case, input channel 1 is output on the first channel, the second output channel uses the special input channel 0, silence. For our second stream, we flip the numbers around to specify that the input be played on the second output channel. You can also adjust the volume of the input channel in the output. My actual command for my first stream is:

nn

1n
play -qV1 -t mp3 - remix 1v0.3 0

nnn

In this case we are attenuating the voltage of the signal by 70{7e898290d39a0d5231795fca98f9d583658ec52d7dd28242e01977702bb4ac4e}. 2 instead of 0.3 would double volume. This lets me adjust the streams for equivalent volume.

nn

play can take any filter chain you can pass to sox, another useful one here is silence trimming. By adding silence -l 1 0.1 1{7e898290d39a0d5231795fca98f9d583658ec52d7dd28242e01977702bb4ac4e} -1 2.0 1{7e898290d39a0d5231795fca98f9d583658ec52d7dd28242e01977702bb4ac4e} to the end of our play command line, we can ask sox to trim any silence longer than 2 seconds to 2 seconds. This may seem odd since we are listening in real time, not recording to a file, but it can help with minimizing delay. Due to packet loss, slow connections, congested links, etc, the stream may stutter at times. Since a large portion of the time the stream is silent, these stutters aren’t noticable to the ear but it means playback was silenced for a few seconds and then resumed. Any of these delays add up and eventually the audio you are listening to could be as much as 5 or 10 seconds behind the metadata printed by fIcy. By telling sox it’s okay to throw away silence, we can keep the audio caught up, minimizing delays.

nn

We can put the commands together in simple shell scripts to simplify calling them.

nn

ems.sh
1n2n3n4n
#!/bin/shnnfPls -v -T 10 -L-1  http://gallowaynow.com:8000/stream.m3u -t n    | play -qV1 -t mp3 - remix 1v0.3 0 silence -l 1 0.0 1{7e898290d39a0d5231795fca98f9d583658ec52d7dd28242e01977702bb4ac4e} -1 2.0 1n

nnnnn

pd.sh
1n2n3n4n
#!/bin/shnnfPls -v -T 10 -L-1  http://gallowaynow.com:8000/gtpd.m3u -t n    | play -qV1 -t mp3 - remix 0 1 silence -l 1 0.0 1{7e898290d39a0d5231795fca98f9d583658ec52d7dd28242e01977702bb4ac4e} -1 2.0 1n

nnn

tmux

nn

Since the metadata is visible, it shows exactly which unit is talking and on which channel, running our two commands in a split tmux window makes since. Especially if you have screen real estate to put this terminal out of the way somewhere. We can script the creation of this window with a simple shell script.

nn

tmux-streams.sh
1n2n3n4n5n6n7n8n
#!/bin/shnntmux new-session -d -s streamsntmux send-keys './ems.sh' 'C-m'ntmux select-window -t streams:0ntmux split-window -vntmux send-keys './pd.sh' 'C-m'ntmux -2 attach-session -t streamsn

nnn

I like send-keys instead of specifying the command to run directly as it lets us ctrl+c the script and then rerun it with up-arrow enter. Handy if I tweak the script or want to launch a fresh copy of stuff instead of having to destroy the tmux session.

nn

tmux screenshot

n

The Often Overlooked Perl Module Pod::Usage

Pod::Usage is one of those boring modules that doesn’t get the attention it deserves. Included in Perl core since 5.6.0, released fourteen years ago, it’s not a new kid on the block. It’s job isn’t sexy, so not lots of talks, blog posts, or tweets about it. There are only 5 ++s for the distribution on metacpan.org. It’s my goto module for all but the most trivial scripts, I probably use it second to only pragmas in scripts. (Haha, I made a funny.)

nn

Pod::Usage generates usage/help output for your scripts by extracting relevant bits from your POD. This means you write usage/help information once for the POD and you get both your man page and -h command line help. Let’s look at its usage by starting with the result. I’ll use otfile, a simple script for serving a file via HTTP from the command line for these examples.

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n18n19n
$ otfile -hnUsage:n    $ otfile [-a -p 1234] nnOptions:n    --auto or -an            Auto port selection. Increments specified port until successful.nn    --port=N or -p Nn            Use specified port, defaults to 1234.nn    --multiple or -mn            Don't exit after serving the file the first time. To serve an            file to multiple people. Requires, CTL+C to exit.nn    --help or -hn            this help informationnn$

nnn

Nothing exceptional here, this is the style and content you would expect from any script on a POSIX like system. We get similarly unsurprising output for incorrect arguments.

nn

1n2n3n4n5n6n
$ otfile --invalid-arg-named-yaakovnUnknown option: invalid-arg-named-yaakovnUsage:n    $ otfile [-a -p 1234] nn$

nnn

So what does the source look like? One of Pod::Usage’s benefits is the simplicity in using it.

nn

1n2n3n4n
use Pod::Usage;nuse Getopt::Long;nnGetOptions( ..., 'help' => sub { pod2usage(1) } ) or pod2usage(2);n

nnn

That’s it in terms of code! You can substitute Getopt::Std or your other argument parser of choice just as easily. This was really cheating though since the real meat of it is in the POD. So anything special there?

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n18n19n20n21n22n23n24n25n26n
=head1 SYNOPSISnn$ otfile [-a -p 1234] <file to serve>nn=head1 OPTIONSnn=over 8nn=item B<--auto> or B<-a>nnAuto port selection.  Increments specified port until successful.nn=item B<--port=N> or B<-p N>nnUse specified port, defaults to 1234.nn=item B<--multiple> or B<-m>nnDon't exit after serving the file the first time. To serve a file to multiplenpeople. Requires, CTL+C to exit.nn=item B<--help> or B<-h>nnthis help informationnn=backn

nnn

Nothing special in the POD. Pod::Usage with the source I used outputs either the POD’s SYNOPSIS section or the SYNOPSIS section and any section titled OPTIONS, ARGUMENTS or OPTIONS AND ARGUMENTS.

nn

That’s it! Now, head over to Pod-Usage on metacpan.org and give it some ++ love.

n

Logging Apple Battery Data with Perl

Inspired by a blog post and the code that goes with it at jradavenport/batlog, I created a perl script for logging Apple laptop battery details last August.

nn

My script logs 11 values using ~67 bytes per record. The oldest log I have begins 288 days ago and is 17,122,966 bytes for an average of 59.4 KB/day or 21 MB/year. The log is missing data points due to things like prolonged hibernation or sleep, theoretical max is around 35MB/year with 10 minute resolution.

nn

The repo is on GitHub.

nn

Sample Graphs

nn

A simple gnuplot script is included in the repo which produces the below graphs.

nn

A couple days of output:nSample Battery Graph

nn

9 months of output:nSample Battery Graph 9 Months

nn

Things get a bit squashed at 9 months but the most interesting value over long durations, battery health, is clearly visible.

nn

The Code

nn

Acquiring the data for this in Perl is exceedingly simple. Apple provides a binary called ioreg for interacting with the I/O Kit Registry. Asking for battery information with the arguments -rc AppleSmartBattery gets us almost JSON:

nn

1n2n3n4n5n6n7n8n9n
+-o AppleSmartBattery      {n      "TimeRemaining" = 36n      "AvgTimeToEmpty" = 65535n      "InstantTimeToEmpty" = 65535n      "ExternalChargeCapable" = Yesn      "FullPathUpdated" = 1401701615n      "CellVoltage" = (4193,4182,4193,0)n      ...

nnn

For our simple needs, massaging the data into proper JSON to use a parser is unnecessary. A regex can cleanly capture the names inside the quotes and their values. We then store the name value pairs in a hash.

nn

1n2n3n
if ( $line =~ m/"([^"]+)" = (.*)$/ ) {n    $data{$1} = $2;n}n

nnn

We create an array of the values we actually care about logging. This is done near the beginning of the script, before running and parsing the output of ioreg. If the amount of data was much larger, our simple parser could be made smarter to only store in the hash the values we care about. With only 39 values though, the slightly increased memory usage in the short lived cron job is preferable to the little bit of extra CPU to check if we want a value before storing it.

nn

1n2n3n4n
my @logged = ( qw(n        CurrentCapacity MaxCapacity Pcnt CycleCount LifePcntn        ExternalConnected IsCharging FullyCharged Temperature CellVoltagen) );n

nnn

We can then take advantage of hash slices to write out just the values we care about:

nn

1n
say $fh join( "t", time, @data{@logged} );n

nnn

@data{@logged} becomes a list of the hash values for the keys earlier specified in @logged.

nn

The full script weighs in at only 28 lines of straight forward code. See just it here.

nn

GitHub Repo

n

In Perl, Debugging with Binary Data Structures

Perl has some really great tools for dumping datastructures when debugging. Data::Printer by GARU is a realitive new comer that’s gained a tremendous following. Of course, everyone also knows Data::Dumper, core since at least perl 5.5 in 2004. What do you do when you need to dump binary data though?

nn

I recently had to do this for the first time so I went looking on the CPAN to see what was available. I found two modules that both made dumping binary data simple. Data:HexDump and a more recently updated Data::HexDumper. Both have similiary simple usage:

nn

1n2n3n4n5n6n7n
use Data::Hexdumper;nuse Data::HexDump;nnmy $binary_data = get_some_bin_data();nnprint hexdumper $data;nprint hexdump $data;n

nnn

I started with the much more configurable and more recent Data::HexDumper. The only draw back was some of my data generated warnings when printing. You could turn this off with either of:

nn

1n2n3n
print hexdumper (data => $binary_data, suppress_warnings => 0);n# ornprint hexdumper $binary_data, { suppress_warnings => 0 };n

nnn

but both were two much typing for a lazy Perl developer. I ended up installing and switching over to Data:HexDump. This module has no configurable options but didn’t generate warnings with my sample data.

nn

For simple situations, Data:HexDump seems a suitable choice at 4k. Even you want control over the output, and there is lots of control offered, Data::HexDumper does it in only 13.6K.

nn

Here’s sample output for both, with default options for Data::HexDumper.

nn

Data::Hexdumper

nn

1n2n3n4n5n6n
  0x0000 : B4 2D 81 35 D0 16 23 4C F4 ED 23 64 60 AB FC 37 : .-.5..#L..#d`..7n  0x0010 : 0C CA 60 C7 3F A2 58 75 53 CD 78 DF D3 0C 16 18 : ..`.?.XuS.x.....n  0x0020 : B7 CD 92 95 A2 E1 54 3A F3 89 7A 88 89 28 05 4A : ......T:..z..(.Jn  0x0030 : 1B 18 C9 2F 61 8D 49 83 E6 2B 18 96 A6 40 8F BA : .../a.I..+...@..n  0x0040 : 44 0D A4 02 1E D2 01 E3 9F 1C 63 47 94 80 9B 74 : D.........cG...tn  0x0050 : 0C D1 63 46 0A 89 B5 1F F3 A7 68 4C 14 1B E6 2E : ..cF......hL....n

nnn

Data::HexDump

nn

1n2n3n4n5n6n7n8n
      00 01 02 03 04 05 06 07 - 08 09 0A 0B 0C 0D 0E 0F  0123456789ABCDEFnn00000000  B4 2D 81 35 D0 16 23 4C - F4 ED 23 64 60 AB FC 37  .-.5..#L..#d`..7n00000010  0C CA 60 C7 3F A2 58 75 - 53 CD 78 DF D3 0C 16 18  ..`.?.XuS.x.....n00000020  B7 CD 92 95 A2 E1 54 3A - F3 89 7A 88 89 28 05 4A  ......T:..z..(.Jn00000030  1B 18 C9 2F 61 8D 49 83 - E6 2B 18 96 A6 40 8F BA  .../a.I..+...@..n00000040  44 0D A4 02 1E D2 01 E3 - 9F 1C 63 47 94 80 9B 74  D.........cG...tn00000050  0C D1 63 46 0A 89 B5 1F - F3 A7 68 4C 14 1B E6 2E  ..cF......hL....n

nn

Alfred 2 Workflow for Audio Device Selection

Alfred V2 has a great new feature called workflows. I’ve been playing with a few others wrote and had ported my old custom commands to workflows but was looking for something to really make use of their power.

nn

Changing OSX Audio device from menubarnChanging the selected audio output device was just the thing to implement as a workflow. OS X has a nice shortcut for doing this without heading to system preferences. Option clicking the speaker in the menu bar well let you change the input or output device, but I wanted to do it easily from the keyboard.

nn

The ‘Script Filter’ option under the input menu is the magic for this workflow. It allows a script to output a list of items for display and selection in the Alfred GUI. You get Alfred’s great predictive auto complete which makes selecting a device a breeze.

nn

Selecting output audio device in Alfred

nn

Output via Growl or Notification Center provides feedback of the completed action.

nn

Growl confirmation

nn

The workflow is pretty simple in Alfred’s workflow editor, it ends up looking like this:

nn

Alfred work flow

nn

Alfred makes sharing workflows insanely easy by throwing all the necessary files for each workflow in a dedicated directory. Right clicking a workflow in Alfred gives an export workflow option which creates a zip file of this directory with the ‘.alfredworkflow’ extension.

nn

Grab SwitchAudio.alfredworkflow

nn

The heavy lifting is done by switchaudio-osx by deweller on GitHub with a simple perl glue script to pass data back and forth between Alfred and switchaudio-osx.

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n18n19n20n21n22n23n24n25n26n27n28n29n30n31n32n33n34n35n36n37n38n39n40n41n42n43n44n45n46n47n48n49n50n
#!/usr/bin/env perlnnuse strict;nuse warnings;nnuse FindBin;nnmy $action = shift || 'list';nmy $direction = shift || 'output';nnif ($action eq 'list') {n    my $devices = get_possibilities($direction);n    print qq{nn};n    print output_device($_) for @$devices;n    print "n";n}nnif ( $action eq 'set' ) {n    my $device = shift;n    print set_device($direction, $device) . "n";n}nnsub get_possibilities {n    my $direction = shift;n    my @devices;n    open( my $fh, '-|', $FindBin::Bin . '/SwitchAudioSource',n        '-at', $direction );n    while ( my $line = <$fh> ) {n        chomp $line;n        $line =~ s/ (Q$directionE)$//;n        push @devices, $line;n    }n    close $fh;n    return @devices;n}nnsub set_device {n    my ( $direction, $device ) = @_;n    open( my $fh, '-|', $FindBin::Bin . '/SwitchAudioSource',n        '-t', $direction, '-s', $device );n    my $output = join "n", <$fh>;n    close $fh;n    return $output;n}nnsub output_device {n    my $device = shift;n    ( my $device_no_space = $device ) =~ tr/ /_/;n    return qq{$deviceicon.pngn};n}n

nn

Fair Trade Coffee Price Premium

Galloway Township considered a resolution last week thatnwould support fair trade practices. The resolution was tablednafter some brief discussion. The cost difference between fair trade and non fairntrade items was brought up and I was curious how much of a difference there was.

nn

The town currently gets most office supplies from WB Mason. I do not know if thisnincludes coffee but at work we get our coffee from WB Mason so as any Geek wouldndo, I made a spread sheet.

nnnnn

Direct Link to Google Docs Spreadsheet

nn

There are only two fair trade items in this category but they are right in linenwith prices for non fair trade varieties. If you are already using one of thennon fair trade blends from the two companies with a fair trade blend, there is 0nprice difference.

nn

This lack of price difference surprised me.

n

ZSH Completion Waiting Dots

ZSH has some pretty nice tab completion but some of the completion modules can make things slow when you have a high latency conection. This can be extra harsh for me as I have failure to complete beeps turned off so I’m not sure if it’s still trying to complete or if there were no matches. When reading some zshrc snippts on Stack Overflow I came accross my favorite new snippit.

nn

This snippet from a paid nerd adds ‘…’ output during completion that is replaced with the completion or removed once the completion engine is finished.

nn

Here’s the snippet:

nn

1n2n3n4n5n6n7n8n
# http://stackoverflow.com/a/844299nexpand-or-complete-with-dots() {n  echo -n "e[31m...e[0m"n  zle expand-or-completen  zle redisplayn}nzle -N expand-or-complete-with-dotsnbindkey "^I" expand-or-complete-with-dotsn

nnn

Also, get yourself in the habbit of including a comment with a url for stuff like this. I once wanted to write a blog post about something I found somewhere and couldn’t remember where I found it online! I’ll never make that mistake again.

n