Thursday, July 11, 2019

Home automation on the cheap, part one

The challenge: automate things around the home, as cheaply as possible.

Back when I started work in IT, home automation was The Next Big Thing that never actually became big. At the time, X10 was the standard of choice, and though there was no way I was spending that kind of money and effort to have my home re-fitted with X10 controllers, I kinda liked the idea. It speaks to my inner nerd.

Roll forward 20+ years, and home automation is suddenly achievable for not too much money or effort. And with voice control!

My initial goal was to have a voice-controlled replacement for my Logitech Harmony universal remote.

Image result for logitech harmony

Being kind-of an audio nerd, I like having the TV sound played through the stereo in the lounge - far better sound than the TV's tinny little speakers. Thing is, it can be hard for every member of the household to remember to turn on the amp and the TV, or which buttons to press to hear TV sound, or the CD player, or the turntable... so the Harmony universal remote's "scenes" function was a great way to turn on the stereo, turn on the TV, switch the stereo's input to the TV, adjust the volume to something sane. Thing is, the Harmony's interface was kind of a pain to use, especially since it relied on having a Windows computer handy to program it. Not great for a Linux household.

I started with a Google home mini, waiting till they were on sale at Officedorks for AUD$45. I added a Broadlink Mini 3 for infrared control of the TV and stereo - that cost about $30.
BroadLink RM Mini3 Smart Wi-Fi IR Remote Controller Smart Home Automation Black

Obviously, your appliances you wish to control must have infrared controls.

At this point I had a capable replacement for the Logitech Harmony. The IHC iPhone app to control the Broadlink was pretty straightforward. You add appliances that the Broadlink can control, then build up scenes... pretty close to how the Harmony worked.

See the pauses between commands? It took a bit of experimentation to get these right... the amp does not respond to having IR commands fired at it too quickly! And if it's just been turned on, it goes through a period where it's not responding... hence the wait times are required. 

Once these scenes are set up, you link your Broadlink account to your Google assistant's account, and all the scenes are visible to Google. So you can say "OK Google, activate watch TV" and it All Just Works. Mainly.

Why "mainly"? Because infrared controls don't have "turn the amp on" or "turn the amp off" signals. It's a power toggle - if it's off, it turns on; if it's on, it turns off. So if your TV and amp get out of step - one is on and one is off - then your scene "Watch TV" will turn one off and one on. So you end up needing scenes called "TV On" and "Amp On" to bring them back into step. Or you resort to using the amp's remote control, or... (horror of horrors!) actually walk over and use the buttons! To make matters worse, the amp's power button is a "hard off" so you couldn't turn the amp back on by infrared if someone had pressed it to turn the amp off. Yeesh!

I ended up replacing the amp with a Denon that still uses infrared, but has separate IR commands for on and off. Also, the Denon sounds beautiful - a really massive improvement over the previous amp. Gotta love Facebook marketplace for cheap second-hand stereo gear! Anyway... this means that for home automation, we can tell the amp to "turn on" which solves one issue. Actually two: I no longer need a graphic EQ to correct the sound of a crappy amp. One less appliance in the lounge room!

Logitech Bluetooth Audio Adapter

I then added a Logitech Bluetooth receiver (about $45) - I stream audio to this from my phone, so I can play Spotify (and podcasts) to it. Works a treat! One little issue... the receiver is always on, so as soon as my phone is in range, it starts streaming the sound to the bluetooth receiver. However, the amp might be off, or receiving sound from the TV input, but my phone doesn't know that... it's just merrily sending audio and I never get to hear it. So what I needed was a way to turn the Bluetooth receiver on only when I'm listening to the Bluetooth input on the amp. Sounds like a candidate for a scene where I turn on the amp, turn on the bluetooth receiver, and select the bluetooth input on the amp... like this!

So, how do we turn on the power to the bluetooth receiver? At the time I set this up, wifi powerpoint controllers were expensive, so I ended up getting some wireless remote controllers like this:
They're cheap, readily available, and pretty good (more later on why they're not "great"). They use the 433 MHz band for their communication, and it works pretty well through walls, along the length of my house. But since the goal here is to automate them, we need a way to control them. Turns out the Broadlink RM Pro incorporates infrared and 433 MHz, so it can control those. I picked one up online for about $35, and it replaced the Mini:

Plug the power adaptor for the Bluetooth reciver in to one of the wireless power controllers, train the Broadlink as to which controller is which:

... and hey presto, we can turn on the bluetooth reciver as part of a scene (and also turn it off as part of a scene called "Bluetooth off"). Yay!

Since the wireless powerpoint controllers come in multiple packs, I used the additional controllers to plug in a few lamps, and now we also have "OK Google, turn on the loungeroom lamps". Further happiness!

So why are the wireless controllers not great? There are two issues: 
  1. the controller for one batch of these interferes with the others... so turning off the loungeroom lights also turns off the bedroom lamp. That's annoying!
  2. they don't have a manual button to control them. You have to use either the Google automation, or the remote control they come with. I'd really prefer a button on them to switch power on or off if needed
Roll forward to the present day, and suddenly there are really cheap wifi-enabled power controllers with power buttons on them. Perfect! I recently acquired this for the princely sum of $15:

It works absolutely fine, setup was easy, and the USB charger port is a nice touch. Its iPhone app makes setup of the controller easy, then link to it from Google and hey presto! Voice control, or optionally, use the Google Assistant app.

Now it seems like Arlec are making their own equivalent. At $32 for a twin-pack at Bunnings, these also look promising, though I haven't yet set these up:

No doubt I'll need another iPhone app to set them up... but once it's set up and linked to Google, I'll never use the app again, so I don't mind. 

The final thing I'd really like to automate is the heating controls... that's a whole 'nother issue. Look out for that in Part 2, if I ever get that working!

Oh, and now I have so many wifi devices hanging off it, the Modem Of Incredible Cheapness is having a hissy fit every 3 days or so. The struggle is real.

Monday, September 11, 2017

NSClient++ and registry keys to allow arguments

Setting up new NSClient++ on WIndows servers, trying to check the status of just one service, ut keep getting the dreaded :

Exception processing request: Request contained arguments (not currently allowed, check the allow arguments option).

SO plenty of people out there with hints for this, but they all involve editing the .ini file - great, except I used the registry method to configure NSClient++. Hmmm. Clint Boessen's blog pointed me in the right direction - set the config key "allow arguments = 1" so I did the closest thing I could think of in the registry, and it worked. Cool:

Wednesday, February 3, 2016

Email from Solaris to Exchange distribution groups

So yes, I need to send email, via mailx, from Solaris boxes, to Exchange distribution groups. Seemed like a better way than embedding a bunch of email addresses into my scripts. Seemed so simple. So wasn't.

I tested:

w | mailx -s "test to one email"

And it works fine.

Then I created an Exchange distribtution group, with me as the sole member, and tested:

w | mailx -s "test to group"

Didn't work. Why?

The trick turns out to be that Exchange distribution groups have, by default, a setting that requires senders to "authenticated" - this seems to mean "part of my Windows domain" - in that they have to be authenticated as Windows users. Clearly, sendmail on Solaris isn't going to to meet this criterion.

The solution: open properties for the distribution group, and click the "MailFlow Settings":

Then double-click "Message Delivery Restrictions":

Un-select "Require that all senders are authenticated". Simples!

Wednesday, May 20, 2015

Space out on Solaris 10

Removing spaces from file names... seems like everyone has their own way to do it. Here's what I ended up putting in my Solaris 10 ~/.bashrc :

function despace()
    while IFS='' read FILE
        NEWFILE=`echo "$FILE" | sed -e 's/ /_/g'`

        if [ "$FILE" = "$NEWFILE" ]
            echo "No spaces detected in [$FILE]"

        if [ -e "$NEWFILE" ]
            echo "$NEWFILE already exists, not going to overwite it"
            echo "Renaming [$FILE] to [$NEWFILE]"
            mv "$FILE" "$NEWFILE"

Use the command like this:

$ ls * | despace
Renaming [CT.SABR test 5.1.dcm] to [CT.SABR_test_5.1.dcm]
Renaming [CT.SABR test 5.10.dcm] to [CT.SABR_test_5.10.dcm]
Renaming [CT.SABR test 5.100.dcm] to [CT.SABR_test_5.100.dcm]

If a file by the name we're changing to already exists, it will not get over-written:

$ ls * | despace
Renaming [CT.SABR_test_5.1.dcm] to [CT.SABR_test_5.1.dcm]
CT.SABR_test_5.1.dcm already exists, not going to overwite it

The IFS='' is needed to catch trailing spaces on filenames (I know, no sane person would do that, but included for the sake of completeness). If you pass it filenames without spaces, it tells you and skips them.

Note: on Linux you can use rename, but that doesn't live on Solaris 10 by default, and I'm working with the tools I have

Monday, December 1, 2014

Stat - just the facts, ma'am

Stat is a useful tool on Linux that wraps the stat system call to return all available info about a file, and prints it out neatly. I wanted an abbreviated output - just atime, mtime and ctime. Rather than pipe stat's output through something else to strip out the lines I didn't want. I used the stat utility's --printf option to output just the bits I wanted:

$ stat --printf="atime: %x\nmtime: %y\nctime: %z\n" /tmp/testfile
atime: 2014-12-02 09:57:01.890339130 +1100
mtime: 2014-12-02 09:58:31.066781362 +1100
ctime: 2014-12-02 09:58:31.066781362 +1100

Yay for stat (and reading the man page once in a while!)

Wednesday, July 16, 2014

Fun with tar

I had an interesting challenge today: we use an application (Pinnacle) that uses tar files to save a collection of patient files as an archive. How can we tell which tar file contains which patients?

For each patient in the archive, there's a file called "Patient", so I initially started by getting these out and grepping them. Because tar doesn't allow a wildcard operator, you have to first build a list of the files (one per patient) in a sub-shell, then pass it to tar:

tar xfO 20131025_03_HD1.1.tar `tar tf 20131025_03_HD1.1.tar| grep 'Patient$'` | grep -i lastname

Note: you'll need to use gtar if you're on Solaris (on Solaris 10 boxes it's installed at /usr/sfw/bin/gtar) so you can extract the file's contents to STDOUT using xfO (that's a capital letter Oh, not a zero).

However, there's an easier way to manage this. At the start of each tar file, there's a file called Institution which stores header information about the patients contained in this archive file. The section we're interested in looks like this:

  PatientLite ={
    PatientID = 98765;
    PatientPath = "Institution_123/Mount_0/Patient_98765";
    MountPoint = "Mount_0";
    FormattedDescription = "CLAUS&&Santa&&&&098765&&SL&&2013-10-23 11:20:03";
    DirSize = 349.607;
  PatientLite ={
    PatientID = 12345;
    PatientPath = "Institution_123/Mount_0/Patient_12345";
    MountPoint = "Mount_0";
    FormattedDescription = "CHRISTMAS&&Mary&&&&012345&&MAG&&2013-10-23 11:20:14";
    DirSize = 262.177;

OK, so it shouldn't be too hard to get this info:

$ gtar xfO 20131025_03_HD1.1.tar Institution | awk -F'=' '$1 ~ /FormattedDescription/ {print $2}'

 "CLAUS&&Santa&&&&098765&&SL&&2013-10-23 11:20:03";
 "CHRISTMAS&&Mary&&&&012345&&MAG&&2013-10-23 11:20:14";

... clean up those ampersands:

$ gtar xfO 20131025_03_HD1.1.tar Institution | awk -F'=' '$1 ~ /FormattedDescription/ {print $2}' | sed -e 's/&/ /g'

 "CLAUS  Santa    098765  SL  2013-10-23 11:20:03";
 "CHRISTMAS  Mary    012345  MAG  2013-10-23 11:20:14";

Now let's write something to catalog a directory full of these tar files:

$ for TARFILE in *.tar; do echo "Filename: $TARFILE"; (gtar xfO "$TARFILE" Institution | awk -F'=' '$1 ~ /FormattedDescription/ {print $2}' | sed -e 's/&/ /g'); done

Filename: 20131025_03_HD1.1.tar
 "EXAMPLE  Fred    012345  SL  2013-10-23 11:20:03";
 "EG  Robert    123456  MAG  2013-10-23 11:20:14";

Filename: 20131025_04_HD1.1.tar
 "CITIZEN  Jeanette    234567  SL  2013-10-23 11:20:03";
 "ALIAS  Dean    345678  MAG  2013-10-23 11:20:14";

Filename: 20131025_05_HD1.1.tar
 "MANCHU  Fu    456789  SL  2013-10-23 11:20:03";
 "KHAN  Ghengis    567890  MAG  2013-10-23 11:20:14";

Hey presto!

Monday, June 2, 2014

Firefox - running multiple instances

If you've ever logged on to the same host from two different displays and tried to run Firefox, you will probably have seen:

Firefox is already running but is not responding. To open a new window, you must first close the existing Firefox process or restart your system

It's because two instances of the Firefox application are attempting to share ~/.mozilla/firefox/some-default-profile. No matter how many combinations of -no-remote and -new-instance I tried, same problem.

Solution: small shell script that creates a new profile each time (we don't need any bookmarks imported or anything, we just want a simple browser to display an HTML page) then deletes it afterwards:



TEMP_PROFILE=`mktemp -d`
$FIREFOX -CreateProfile "$PROFILE_NAME $TEMP_PROFILE" 2>/dev/null

Tested and working on Linux and Solaris 10. The idea came from here but I couldn't get to the shell script from that post (busted link?) but I figured this was probably what he/she meant :-)