Saturday, January 11, 2014

Chromecast as WiFi Extender

Our internet provider brought us a new modem / router today, which ended up with me re-configuring our home network. While doing so, I noticed that our Chromecast was showing up as a wireless network in my phones list of wireless networks. This got me thinking, can the ChromeCast somehow be used as a wireless range extender, does Google have plans to add such useful functionality to the ChromeCast? It appears to already have the wireless hardware.

Monday, January 6, 2014

Chromecast Auto Tune Doesn't Work

I recently picked up a Google Chromecast thinking it made more sense than paying a little less for an HDMI cable long enough to go from our home office to the living room television.

The only problem is that even though our TV has multiple HDMI inputs, our TV doesn't support something called HDMI-CEC, which is required in order for Chromecast to automatically tune the TV to the HDMI port Chromecast is connected to. Meaning we would still have to find the TV remote or walk to the TV to use Chromecast.

Luckily I found a solution, the Kinivo 501BN HDMI Switcher, which does support HDMI-CEC, meaning I'm able to plug the cable box and Chromecast into the switcher, then the switcher into the TV, and Chromecast can tell the switcher to show Chromecast content.

It's kind of funny really, now when someone in the house says "Where's the remote?" I can answer with "Call it" without being a smart ass.

Monday, November 18, 2013

Arduino Powered Vertical Garden

I started setting up a Vertical Garden in the back yard using PVC and landscape timbers. I plan to grow romaine lettuce and a few herbs for starters. At the moment there are just some plants I picked up at Home Depot for testing, I didn't bother to transplant them from the containers they came in for now. Eventually I plan to use net pots or net cups in the system and have a semi-hydroponic setup going. By semi-hydroponic I mean starting plants in small extremely fertle bundles of organic matter, wrapped in a mesh that's strong enough to keep the organic matter in place but weak enough to let roots grow out into a medium in the net pots. My plan is that the small amount of organic matter will be enough for water sprinkled on the system to leach out into the pipes and feed the rest of the roots. So all I'll have to do is setup watering automation and harvest once growth stunts.

Here's a couple of photos of the system at the moment. I originally went with foggers and a 12 port drip irrigation manifold, but there was not enough pressure in my house spigot to push the foggers. I experimented with some sprayers I had and they're wasting a lot of water because the spray pattern is too large. I have one basket mister on the system and it looks like the way to go. I'm thinking a 6 port manifold and 6 of those misters will work fine.

Now, the title does say "Arduino Powered" vertical garden, so I suppose I should get to the Arduino part. I haven't actually started on that part yet, though here are my plans with it. I'm going to hook at solar cell scavanged from a solar powered deck light to the analog input of the Arduino for use as a sun meter. The more voltage I see on that analog input, the brighter it is outside. I live in Florida, so it's always humid... The brighter it is, the more water I need to hit the plants with.

I was originally going to use a simple garden hose timer to water everything, but given I have the yard sprinklers on another Arduino Mega in the house anyways, I might as well hook this up as well.

Anyways, I'm having a tough time finding local suppliers of 3 3/4" net pots to fit in my 3 1/2" holes. So I'll have to order them online and wait for a bit. On the bright side, my Home Depot had Dig brand drip irrigation supplies on sale for about 80% off! 50ft rolls of 1/4" microtube for $0.99 instead of $4.99, score!

Sunday, November 17, 2013

RHT03 / DHT-22 Temperature / Humidity Sensor Housing

I have been using the DHT-22 temperature and humidity sensors
throughout the house for monitoring temp/humidity with an Arduino Mega. They're kind of awkward to place though. I discovered with a couple of snips with an exacto knife however, the DHT-22 fits quite nicely in a one port single gang wall plate, which in turn can be mounted to a single gang cut in box in the wall to protect connections.

Here's some photos of how it turned out.

Saturday, November 16, 2013

Arduino Solar Sensor

I'm thinking about taking this cheap 1.2VDC solar powered garden light's solar cell and making something I can hook up to my Arduino mega to record how much sun I get where I'm at. In theory I should be able to monitor the voltage coming from the solar cell on an analog input pin of the arduino. I don't anticipate the cell ever producing the 5VDC limit on the Arduino, but maybe I should add a voltage regulator as a protection measure, say if the cell short circuits.

What to do with the voltage, albeit tiny current, generated from the solar cell though? I could charge a backup battery for the house's Arduino Mega.

Sunday, January 20, 2013

auth.log Service Restart

I tried to incorporate my auth.log file into logrotate and didn't use copytruncate in my logrotate.d configuration file, so I ended up with a missing auth.log file. I tried to recreate auth.log by copying auth.log.0 and chown'ing it syslog:adm, but I ended up with an auth.log that wasn't being updated because the authentication daemon no longer had a handle to the file.

Rather than reboot the whole server, I wanted to restart the service that writes to auth.log. I found a reference to rsyslog, but I was a couple of versions of Ubuntu later than the 10.04 mentioned in that conversation. (Ubuntu 12.04 LTS) so I ended up getting "rsyslog: unrecognized service" when I tried that.

I ran "service --status-all" to get a list of services on the machine and "sysklogd" looked promising so I tried restarting it.

user@server:~# service sysklogd restart
 * Restarting system log daemon...                                       [ OK ]

Then I exited SSH and logged back in, to find that my auth.log file was getting written to again.

Saturday, January 19, 2013

regex capture $1 refers to no regex

I discovered this warning in my Apache log after adding a command via mod_include which utilized awk. When I placed backslashes before the dollar signs in my html page, the error disapeared.

To

awk '{print \$1\"/\"\$2\"/\"\$3}'

From

awk '{print $1\"/\"$2\"/\"$3}'

Wednesday, January 26, 2011

Simple URL Shortening Script

If you're running Apache and are able to use the mod_asis module, it's very easy to setup a simple and efficient URL shortener. This URL shortening script doesn't require any sort of database and it doesn't have the overhead of something like PHP added to every single redirection.

If you read the documentation for mod_asis, the fundamentals of how this all works may be immediately obvious. If not, basically mod_asis lets you have a sort of static response cache. A lot like having a static HTML cache, but with HTTP headers.

Assuming you have your URL shortening domain setup and you have a nice empty DocumentRoot for your new URL shortening service, the first thing you need to do is create a directory named "stubs". If you want to setup some sort of UI to create new shortened URLs with setup your permissions on stubs so that PHP can write to stubs, but nobody else can.

Within the stubs directory create an htaccess file (ideally you'd use Directory or Location containers in your VirtualHost, but for example sake I'm using htaccess).
Within that htaccess file, add the fillowing line.

SetHandler send-as-is


What that line does, is force every requested file to pass through mod_asis, which if you've read the documentation for mod_asis you know that other than adding a Date and Server header to the response, mod_asis just sends the file to the visitor as-is.

So, if I put a file in that directory named "abc123" with the following contents, I'll basically be given a 302 redirect from the server pointing me to google.com

NOTE: There are two newlines after the Location line to signal the end of HTTP headers. This is important

Status: 302
Location: http://www.google.com/



So basically, at this point I could distribule the shortened URL "http://domain/stubs/abc123" and it would redirect visitors to google.com; This isn't all that nice though, as I have "stubs" in the URL and that sort of defeats the purpose of a shortened URL.

That's why in the parent directory of stubs, AKA the DocumentRoot, I add the following to the htaccess file.

RewriteEngine on
RewriteBase /
RewriteRule ([a-f\d]{1,8})$ stubs/$1 [L]


Now I can distribute my shortened URL as "http://domain/abc123" and it will redirect to google.com; I could still distribute the URL with stubs in it if I wanted and it would still work for both shortened URLs.

At this point I have a small, simple, and efficient URL shortener. I have to manually go into my stubs directory and add a new file every time I want to shorten a URL though.

For that, I have the following simple PHP script with a bulk shortened URL capable UI.

<!doctype html>  
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>My URL Shortener</title>
</head>

<body>
<div id="container">
<header>
<h1>My URL Shortener</h1>
<p>Enter as many URLs as you want, one per line.</p>
</header>
<div id="main">
<?php
/*
Contains backwards compatibility code;
If you comment this include() out and don't get any errors, it's safe to leave it out.
*/
include('./lib.php');

if( ! empty($_POST['u']))
{
$url_list = '<ul>';
foreach(preg_split('#[\r\n\f]+#', $_POST['u'], -1, PREG_SPLIT_NO_EMPTY) as $_url)
{
$url = parse_url($_url);
if($url && http_build_url('', $url) == $_url)
{
$crc = sprintf('%x', crc32($_url));
if( ! file_exists("./stubs/{$crc}"))
{
file_put_contents("./stubs/{$crc}", "Status: 302\nLocation: {$_url}\n\n\n");
}
printf('<li><a href="http://%1$s%2$s%3$s">http://%1$s%2$s%3$s</a> » %4$s</li>',
$_SERVER['HTTP_HOST'],
str_replace('//', '/', dirname($_SERVER['REQUEST_URI']) . '/'),
$crc,
$_url
);
}
}
if(strlen($url_list) > 4)
{
echo $url_list, '</ul>';
}
}
?>
<form action="index.php" method="post">
<fieldset>
<legend>URL</legend>
<textarea name="u" id="u" rows="10" style="width:400px;"></textarea>
<p><input type="submit" name="s" id="s" value="Shorten!"/></p>
</fieldset>
</form>
</div>
<footer>© Me; 2011</footer>
</div>
</body>
</html>


Also, because the PHP function "http_build_url" is a PECL function, I have the following code I include in "lib.php".

<?php
if (!function_exists('http_build_url'))
{
define('HTTP_URL_REPLACE', 1); // Replace every part of the first URL when there's one of the second URL
define('HTTP_URL_JOIN_PATH', 2); // Join relative paths
define('HTTP_URL_JOIN_QUERY', 4); // Join query strings
define('HTTP_URL_STRIP_USER', 8); // Strip any user authentication information
define('HTTP_URL_STRIP_PASS', 16); // Strip any password authentication information
define('HTTP_URL_STRIP_AUTH', 32); // Strip any authentication information
define('HTTP_URL_STRIP_PORT', 64); // Strip explicit port numbers
define('HTTP_URL_STRIP_PATH', 128); // Strip complete path
define('HTTP_URL_STRIP_QUERY', 256); // Strip query string
define('HTTP_URL_STRIP_FRAGMENT', 512); // Strip any fragments (#identifier)
define('HTTP_URL_STRIP_ALL', 1024); // Strip anything but scheme and host

// Build an URL
// The parts of the second URL will be merged into the first according to the flags argument.
//
// @param mixed (Part(s) of) an URL in form of a string or associative array like parse_url() returns
// @param mixed Same as the first argument
// @param int A bitmask of binary or'ed HTTP_URL constants (Optional)HTTP_URL_REPLACE is the default
// @param array If set, it will be filled with the parts of the composed url like parse_url() would return
function http_build_url($url, $parts=array(), $flags=HTTP_URL_REPLACE, &$new_url=false)
{
$keys = array('user','pass','port','path','query','fragment');

// HTTP_URL_STRIP_ALL becomes all the HTTP_URL_STRIP_Xs
if ($flags & HTTP_URL_STRIP_ALL)
{
$flags |= HTTP_URL_STRIP_USER;
$flags |= HTTP_URL_STRIP_PASS;
$flags |= HTTP_URL_STRIP_PORT;
$flags |= HTTP_URL_STRIP_PATH;
$flags |= HTTP_URL_STRIP_QUERY;
$flags |= HTTP_URL_STRIP_FRAGMENT;
}
// HTTP_URL_STRIP_AUTH becomes HTTP_URL_STRIP_USER and HTTP_URL_STRIP_PASS
else if ($flags & HTTP_URL_STRIP_AUTH)
{
$flags |= HTTP_URL_STRIP_USER;
$flags |= HTTP_URL_STRIP_PASS;
}

// Parse the original URL
$parse_url = parse_url($url);

// Scheme and Host are always replaced
if (isset($parts['scheme']))
$parse_url['scheme'] = $parts['scheme'];
if (isset($parts['host']))
$parse_url['host'] = $parts['host'];

// (If applicable) Replace the original URL with it's new parts
if ($flags & HTTP_URL_REPLACE)
{
foreach ($keys as $key)
{
if (isset($parts[$key]))
$parse_url[$key] = $parts[$key];
}
}
else
{
// Join the original URL path with the new path
if (isset($parts['path']) && ($flags & HTTP_URL_JOIN_PATH))
{
if (isset($parse_url['path']))
$parse_url['path'] = rtrim(str_replace(basename($parse_url['path']), '', $parse_url['path']), '/') . '/' . ltrim($parts['path'], '/');
else
$parse_url['path'] = $parts['path'];
}

// Join the original query string with the new query string
if (isset($parts['query']) && ($flags & HTTP_URL_JOIN_QUERY))
{
if (isset($parse_url['query']))
$parse_url['query'] .= '&' . $parts['query'];
else
$parse_url['query'] = $parts['query'];
}
}

// Strips all the applicable sections of the URL
// Note: Scheme and Host are never stripped
foreach ($keys as $key)
{
if ($flags & (int)constant('HTTP_URL_STRIP_' . strtoupper($key)))
unset($parse_url[$key]);
}


$new_url = $parse_url;

return
((isset($parse_url['scheme'])) ? $parse_url['scheme'] . '://' : '')
.((isset($parse_url['user'])) ? $parse_url['user'] . ((isset($parse_url['pass'])) ? ':' . $parse_url['pass'] : '') .'@' : '')
.((isset($parse_url['host'])) ? $parse_url['host'] : '')
.((isset($parse_url['port'])) ? ':' . $parse_url['port'] : '')
.((isset($parse_url['path'])) ? $parse_url['path'] : '')
.((isset($parse_url['query'])) ? '?' . $parse_url['query'] : '')
.((isset($parse_url['fragment'])) ? '#' . $parse_url['fragment'] : '')
;
}
}
?>


That just gives me a simple textarea that I can enter a list of URLs into, and automatically have stubs for short URLs written to the stubs directory and get a list of shortened URLs back.

Monday, January 24, 2011

Google Safe Browsing Wordpress Dashboard Module

I wrote a small dashboard module for Wordpress this morning which automatically fetches the Google Safe Browsing report for the current domain and displays the results on the dashboard. This is a lot more convenient than going to the safe browsing page yourself periodically if you aren't using a browser with the reporting built-in.

The project is named wp-google-safe-browsing-dashboard and the plug-in is available for download at Google Code. It's a nice simple plugin, just upload the zip file using your Wordpress plug-ins manager, activate it, and you're good to go!

Please, no applause, just throw money.

Saturday, January 22, 2011

$18.50 Average Adsense Page RPM

I've been experimenting with a sort of "bare bones" layout with articles for a few weeks now. I have a half-dozen websites using the layout and the average Adsense Page RPM is $18.50 USD. To answer the obvious question, no these aren't articles that use ridiculously high paying no-chance-for-the-little-guy-to-compete-competition keywords either.

Basically, it's a 500-600 word article centered on the page with some navigation links on top, a descriptive heading, a paragraph of introduction text, a 728x90 advertisement, the entire article text, then some links to other articles and websites on the bottom.

There are no layout graphics at all in this layout, only an occasional article-relevant graphic within the article text from time to time. No sidebars, navigation is kept above the heading, and down in the footer. I haven't been adding any links within the article text to relevant articles.

There are 3 sizes of black text, normal, h1, and h2 sizes. The background is solid white and the links are the default blue. The main heading is centered, the rest of the text is left-aligned within the centered 800 pixel column.

The websites use almost no bandwidth, CPU to generate the pages is minimal.

It will be interesting to see if this layout survives, it really does give new meaning to "content is king".

Sunday, January 9, 2011

Text Ads or Image Ads

A question I see a lot is "Which is better with Adsense, text ads or image ads?" and the honest answer is, it depends.
Sometimes a layout will dictate whether you can use text ads or rich media ads. There are certain scenarios where using text ads would break the Adsense TOS whereas using image ads wouldn't.
Another thing to consider is which one would fit better with the placement. Sometimes using one type or the other just doesn't make any sense because it sticks out like a sore thumb rather than a piece of jewelry.

In any event, I've gone ahead and gathered my statistics for the entire year of 2010 and listed each of the Adsense ad types in order of best performing to worst performing for each of the categories Adsense tracks.

Page Views


  1. Image Ads
  2. Text Ads
  3. Flash Ads
  4. Animated Image Ads
  5. Rich Media Ads

CTR


  1. Animated Image Ads
  2. Text Ads
  3. Image Ads
  4. Flash Ads
  5. Rich Media Ads

CPC


  1. Rich Media Ads
  2. Flash Ads
  3. Image Ads
  4. Animated Image Ads
  5. Text Ads

Page RPM


  1. Rich Media Ads
  2. Image Ads
  3. Animated Image Ads
  4. Text Ads
  5. Flash Ads

Estimated Earnings


  1. Image Ads
  2. Rich Media Ads
  3. Text Ads
  4. Animated Image Ads
  5. Flash Ads

Thursday, January 6, 2011

Blocking Low Paying Adsense Categories

I was looking through the new Adsense interface today (the version with /v3/ in the URL) and found out that the control over blocked ads is a lot better than I remember. Under the "Allow and Block Ads" tab there are two awesome options in "Blocking OptionS" section of the sidebar, "General Categories" and "Sensitive Categories".

Sensitive Categories has things like dating, politics, religion, etc. Whereas General Categories has everything else.

Both of these sections show a list of possible Adsense categories along with what percentage of the ads you've displayed came from each category, and how much of your Adsense revenue came from each category. Most of the categories also break down into multiple sub-categories.

As soon as I found it I immediately realized I had a couple of categories with bad impression/revenue relationships. For instance I had one category that was accounting for 13% of impressions, however it only accounted for 3.8% of revenue. In comparison there are other categories that account for 6.9% of revenue on 3.3% of impressions and 4.5% on revenue on 2.5% of impressions.

So, I went ahead and blocked the categories with horrible impression to revenue relationships. I figure that having more ads from more profitable categories being displayed will translate into more money. Since Adsense has much improved reporting in V3 it will be easy to see just how well my changes do in the future.

There is a 50 category limit on the number of Adsense categories that can be blocked.

Saturday, November 13, 2010

client denied by server configuration: .htaccess

I was getting the error "client denied by server configuration: /.htaccess" in one specific location recently and I wasn't sure what was going on. Permissions allowed the .htaccess file to be read, because the directives set in that .htaccess were taking effect. There was also no special reason for anyone to try and navigate directly to this directories .htaccess file.

It turns out, that the problem had to do with mod_autoindex. The directory in question is setup to use mod_autoindex and list a bunch of changelogs in the directory. For whatever reason, mod_autoindex wants to access .htaccess while it's building a list of files and since .htaccess files are disallowed for all in the normal Apache configuration, it's getting that permission error.

I've found that adding .htaccess to the IndexIgnore directive will stop this error from happening. Since I also have mod_autoindex ignore the link to a parent directory, my IndexIgnore directive looks like this.

IndexIgnore .. .htaccess

Sunday, November 7, 2010

Select Odd Rows in MySQL

Any time you can use a bitwise operator in place of a function call, it's a good thing. Selecting odd rows from a table in MySQL that have an auto_increment primary key is no exception.

SELECT * FROM mytable WHERE id & 1;


If you're unfamiliar with a bitwise AND, basically what it says is return true of the left operand has the same bits set as the right operand. The number 1 only has one bit set, and conveniently enough that one bit is the same bit that will only be present in odd numbers.

Wednesday, November 3, 2010

Blogger Double Post Bug

Decided to witch back to the default Blogger templates today. I'd stripped out all of the inline management tools when I created the old layout because I never needed them back then. I just started to miss them too much these days though, they make a lot of things easier.

In any event, I'd been putting this off for awhile because when I went to use the blogger template designer, I was getting a double post bug where my posts would show on the page twice whether I was on the index or just a single post page.

I couldn't seem to find an easy way to remove the extra posts widget so I just said screw it. Today though I got tired of putting it off, and decided to dig into the HTML of the template and hunt down the obviously present second posts widget.

From the looks of things, it just wasn't recognizing my posts template when it reverted to the default templates. It was looking at my posts widget as if it were a HTML widget or something other than posts, so it was adding a new posts widget. Though, the editor for my widget was still that of a posts widget, so there was no remove this widget button.

So, I singled out my own posts widget, the one with an id of "blog2", and removed the entire widget wrapper and all of the includables. Worked like a charm.

Tuesday, October 19, 2010

RSS Feed for SVN Changelog

I wanted to create an RSS feed for the SVN logs of a wallpaper site script lately. I've been keeping track of my progress on the feeds over at Ozzu in this thread.

So far I have the feed and an archive of the revisions being automatically generated and uploaded to the remote server daily.

Having an RSS feed for the SVN commit history presented an interesting problem where I couldn't really update the script_version field in the scripts schema.sql file without clogging up my revision history with worthless updates about the version number being updated.

So, I wrote a small script that exports a copy of the script, retrieves the SVN revision number from the repo, then updates schema.sql with the correct version number before finally packaging script for me.

In case anyone's curious, here's the code for that. This little script lives in my working copy on my workstation, not under version control.

#!/bin/bash

echo "Exporting HEAD revision..."
svn -q export http://192.168.1.102/svn/wallpaper-script

echo "Updating working copy..."
svn -q update

php -r '
$properties = `svn info -r HEAD`;
if(preg_match("#^revision:\s*(\d+)\s*$#im", $properties, $revision))
{
echo "Updating script version in schema.sql to 2.{$revision[1]}...", PHP_EOL;
$schema = file_get_contents("wallpaper-script/install/schema.sql");
$schema = str_replace("script_version'\'', '\''2.0'\'')", "script_version'\'', '\''2.{$revision[1]}'\'')", $schema);
file_put_contents("wallpaper-script/install/schema.sql", $schema);

echo "Compressing package...", PHP_EOL;
shell_exec("tar zcf wallpaper-site-script_2.{$revision[1]}.tar.gz wallpaper-script");
}
'

echo "Cleaning Up..."
rm -rf wallpaper-script

echo "Done."

Saturday, February 6, 2010

gedit Loads Slow

After a recent Ubuntu install I was having a problem with gedit loading slowly. When I would open gedit, I would geta blank gedit window with no toolbar buttons or text area.

The way I fixed this issue was to disable the file browser pane plugin from Edit -> Preferences -> Plugins. Once I disabled the file browser pane plugin I was able to get gedit to startup faster, immediately even.

Thursday, November 26, 2009

Suppress rm No such file or directory

rm: cannot remove `*.html': No such file or directory


If you have a cron job that deletes files daily or on another schedule you probably don't want to get the email that tells you something like that. There's always the option of redirecting the output to /dev/null but then you're screwed if there's any other errors that you actually want to know about.

Instead, use the -f or --force flag of the rm program. That flag literally means ignore nonexistent files, never prompt. So instead of getting that cannot remove error, it will just go about business as usual.

Instead of the following to delete two types of files,
rm /path/to/*.html; rm /path/to/*.html.gz


Use the following to delete them and not have to worry if one type doesn't exist.
rm -f /path/to/*.html; rm -f /path/to/*.html.gz

Monday, November 2, 2009

svnserve.conf: Option expected

If you get this error when trying to checkout an SVN repository you likely have whitespace before one of the settings in the svnserve.conf file the path in the error refers to. There should be a number in the error, which will tell you what line the whitespace is on.

For instance, the following error is given if I remove the hash from the beginning of the passwd line and leave the whitespace there between the beginning of the line and the start of the option.

svnserve.conf:20: Option expected

Saturday, October 31, 2009

X-Pad: avoid browser bug

If you're looking for an explanation to this "X-Pad: avoid browser bug" header you should feel lucky. The X-Pad header is a work around Apache uses for a bug in really old versions of Netscape and it only shows up if there's a chance the 256th or 257th byte of a response is a newline.

It's a junk header, all it does is prevent the 256th and 257th byte of the response from being a newline. If Apache didn't do this, old versions of Netscape would hang.

It's a wonder why this is still part of Apache, we're talking really old versions of Netscape here.