Wednesday, July 2, 2014

(#13) Esper Code - Esper's Main 

Make it work, make it work right, make it work fast -- in that order.  Here's the code that's running two simple rules.  As mentioned earlier, the Esper documentation lacks on step-by-step directions. So this post will try to remedy that.

I can add additional detail if requested, but I'll skip over the basics of downloading Esper, installing the JARs, and creating the libraries and dependencies for NetBeans (v7.4) and instead jump right into the code:


static void main (String args[])

Here's my main(), so far:


public static void main(String[] args) 
{
    //The Configuration is meant only as an initialization-time object.
    Configuration cepConfig = new Configuration();
    cepConfig.addEventType( "HHBStatusEvent", HHBStatusEvent.class.getName() );

    // We setup the engine
    EPServiceProvider cep = EPServiceProviderManager.getProvider( "myCEPEngine", cepConfig );
    EPRuntime cepRT = cep.getEPRuntime();


    // ----------------------------------------------------------------------------------------------------
    // Creste and regsiter the EPL for someone leaving
    String  anEPLQuery = "SELECT * FROM PATTERN " +
            " [  every eventOne = HHBStatusEvent(macAddress='0000012467',deviceStatus='OPEN') " +
            " -> eventTwo   = HHBStatusEvent(macAddress='000000B357',deviceStatus='OPEN' ) " +
            " -> eventThree = HHBStatusEvent(macAddress='000000B357',deviceStatus='CLOSED' ) ].win:time( 5 minutes )";

    EPAdministrator cepAdm = cep.getEPAdministrator();
    EPStatement departureStatement = cepAdm.createEPL( anEPLQuery );

    // ----------------------------------------------------------------------------------------------------
    // Creste and regsiter the EPL for someone arriving
    anEPLQuery = "SELECT * FROM PATTERN " +
            " [  every eventOne = HHBStatusEvent( macAddress = '000000B357', deviceStatus = 'OPEN' ) " +
            " -> eventTwo = HHBStatusEvent( macAddress = '0000012467', deviceStatus = 'OPEN' ) " +
            "-> eventThree = HHBStatusEvent( macAddress = '000007AAAF', deviceStatus = 'MOTION' ) ].win:time( 10 minutes )";
    EPStatement arrivalStatement = cepAdm.createEPL( anEPLQuery );



    //
    // Get the MQTT Stuff ready
    MQTTClient  mqttClient = new MQTTClient();
    mqttClient.setBrokerURL( "tcp://192.168.1.11:1883" );
    mqttClient.setClientID( "CEP Engine 1" );

    //
    // We need to create the Callback Class next - so we can pass it into MQTT
    HHBStatusEventGenerator  statusEventSource = new HHBStatusEventGenerator();
    mqttClient.setCepRuntime( cepRT );               // Do this first!

    // 
    // Don't start subscribing yet!  A bit more setup...
    // Register the callback
    DepartureEventListener departureEventListener = new DepartureEventListener();
    departureEventListener.setTheMQTTClient( mqttClient );
    departureStatement.addListener( departureEventListener );

    ArrivalEventListener arrivalEventListener = new ArrivalEventListener();
    arrivalEventListener.setTheMQTTClient( mqttClient );
    arrivalStatement.addListener( arrivalEventListener );


    // 
    // Now -- Connect to the broker and start subscribing!
    mqttClient.connect( "tcp://10.1.1.17:1883",  "CEP Engine 1" );
    mqttClient.subscribe( "HHB/STATUS" );

    while (true)
        try {
            Thread.sleep( 1000L );
        } catch (InterruptedException ex) {
            Logger.getLogger(CEPMQTT_1.class.getName()).log(Level.SEVERE, null, ex);
        }

}   

Bear in mind, my Esper experience totals up, as of this writing, at 90 minutes. So take these comments as "directional" and an attempt to be helpful. Your mileage may vary.

The first four lines are straight from the examples. Create a Configuration object, then register the POJO event class and save off a reference to the CEP Runtime object. In order to insert MQTT events into the stream, we'll need to invoke a method off the CEP Runtime object. 

The EPL Queries to Detect the Event Patterns

The next three lines, from anEPLQuery to departureStatement, create the EPL query for an event trigger.

String  anEPLQuery = "SELECT * FROM PATTERN " +
            " [  every eventOne = HHBStatusEvent(macAddress='0000012467',deviceStatus='OPEN') " +
            " -> eventTwo   = HHBStatusEvent(macAddress='000000B357',deviceStatus='OPEN' ) " +
            " -> eventThree = HHBStatusEvent(macAddress='000000B357',deviceStatus='CLOSED' ) ].win:time( 5 minutes )";

Consult the Esper documentation but this is the syntax for triggering on three events in a specific sequence within a 5 minute time window.

Recall each Eaton Home Heartbeat sensor is assigned a unique (Zibgee) MAC address. Sensor '0x12467' is the back door, sensor '0x0B357' is the garage door.

We've talked about it earlier, but this statement says "watch for a door (12647) open event followed by a garage door open event followed by a garage door closed event -- all three occurring within a 5 minute window.

(The next lines work to create a similar EPLQuery for an arrival event, but with an extra sensor state tossed in and a 10 minute window.)

Setup the MQTT Client Specifics 
Slide down to:
    //
    // Get the MQTT Stuff ready
    MQTTClient  mqttClient = new MQTTClient();
    mqttClient.setBrokerURL( "tcp://10.1.1.17:1883" );
    mqttClient.setClientID( "CEP Engine 1" );


And were setting up the Paho MQTT routines. My MQTT Broker is on  a server at 10.1.1.17, listening on the default port of 1883.  That brings us to:


    //
    // We need to create the Callback Class next - so we can pass it into MQTT
    HHBStatusEventGenerator  statusEventSource = new HHBStatusEventGenerator();
    mqttClient.setCepRuntime( cepRT );               // Do this first!


My HHBStatusEventGenerator class generates HHBStatusEvents.  The code will be posted but, no surprise, this class subscribes to MQTT HHB/STATUS events and then calls Esper's cepRT.sendEvent() method.


Do This When the Event Pattern is Detected
And that's how Esper will start streaming in events.  But not quite yet. The lines:

    // 
    // Don't start subscribing yet!  A bit more setup...
    // Register the callback
    DepartureEventListener departureEventListener = new DepartureEventListener();
    <...>
    departureStatement.addListener( departureEventListener );

    ArrivalEventListener arrivalEventListener = new ArrivalEventListener();
    <...>
    arrivalStatement.addListener( arrivalEventListener );


create and register Event Listeners with Esper.  A method in each listener class will be called with the event pattern is detected.  

Do Forever
At this point, the setup is complete and we can tell MQTT that we're ready to start subscribing to the event stream.

 // 
    // Now -- Connect to the broker and start subscribing!
    mqttClient.connect( "tcp://10.1.1.17:1883",  "CEP Engine 1" );
    mqttClient.subscribe( "HHB/STATUS" );

    while (!hellIsFrozenOver)
        try {
            Thread.sleep( 1000L );
        } catch (InterruptedException ex) {
            Logger.getLogger(CEPMQTT_1.class.getName()).log(Level.SEVERE, null, ex);
        }

}   


That's about it.  At this point, conceptually:
  • When an MQTT HHB/STATUS event arrives, it's payload is extracted and a corresponding HHBStatusEvent object is instantiated
  • This HHBStatusEvent object is added to Esper's event stream
  • Esper watches the event stream for a pattern match against the 2 EPL statements (one arrival pattern, one departure pattern)
  • If a pattern match is detected, a method in the matching Listener object is invoked
  • At this point, it's up to us to decide what action to take on a pattern match



GOSUB 200 - The FTDI Device Driver 

I apologize for the interruption, but after spending a few hours on the 'net looking for a solution, I need to put down the information I've found in a spot where I can find it.  And that's right here.


If it's not broken...
With kernel 3.12, the FTDI device driver was changed to remove the ability to pass in a vendor and product ID.  The Eaton Home Heartbeat system has an FTDI USB-Serial chip embedded in it, with a product ID that's unknown to the current device driver.  

Earlier device driver releases let you overcome that by passing in the two new IDs:

root@pi# modprobe ftdi_sio vendor=0x0403 product=0xde29


That doesn't work anymore.  The correct way, as of this writing in July 2014, is to do the following. With device unplugged, go root, then:

root@pi# modprobe ftdi_sio
root@pi# echo 0403 DE29 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id

These two command should execute without error and return immediately. If they do not, something else is amiss.

Try these manually at first to gain confidence that things are working.  Once you're sure, you can automate the driver configuration at boot time.



(Boot. Das Boot. Go SUB. Submarine, Das Boot. Get it? Huh???  Right???


I tell you, it all hangs together. I know at times it seems like a huge stretch, but I want you to know I take this whole 'blogging' stuff seriously!


If I didn't, I wouldn't kill hours searching for a movie poster about an obscure film with subtitles.)


Loading the FTDI Device Driver at (Das) Boot Time

You need two new files, a shell script and a udev rules file.  Create this shell script (owned by root:root) and plunk it in /etc (name it what you wish. Mine's named "hhb_ftdi.sh"

#!/bin/sh
/sbin/modprobe -r ftdi_sio
/sbin/modprobe ftdi_sio
echo 0403 de29 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id




Now move into /etc/udev/rules.d and create a rules file. Mine's called "95-hhb_ftdi.rules"

# Rules for hotplugging Eaton Home Heartbest with FTDI (0x0403 0xDE29) Serial Adapter
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="de29", RUN="/etc/hhb_ftdi.sh"
#
# These may be necessary to setup the correct permissions
KERNEL=="ttyUSB0", SYMLINK="hhb", GROUP="dialout", MODE="0660"
# KERNEL=="ttyUSB1", GROUP="dialout", MODE="0660"


The name of the rules file isn't too important. Just make sure it ends with ".rules".

Note the following - use "ATTR{idVendor}" not "SYSFS{idVendor}" as you'll find it out on the 'net.  ATTR has replaced SYSFS.  You'll get an error in /var/log/daemon.log stating "SYSFS{iDVendor} unknown key" or something to that effect.

Watch your "==" placement. Don't use the assignment "=" operator.  Check your spelling!

Note that this rule fires off the shell script.  Make sure the names match!  Note that I create a symbolic link, "/dev/hhb" to "/dev/ttyUSB0".  You don't have to do that if you don't want to.  In fact, start with the KERNEL line commented back out and see what you get.

Debugging udev rules is a known challenge.  Scan the kernel, syslog and daemon.log in /var/log for errors if things aren't working.

If all goes well, you're Eaton Home Heartbeat device will be ready and waiting for you on either port "/dev/ttyUSB0" or "/dev/hhb" when you reboot.






Tuesday, June 24, 2014

(#12) ESP 

And not the whacko stuff JB Rhine wrote about - but Event Stream Processing. From now on, when I type ESP, I mean Event Stream Processing and not Extrasensory Perception.



I've been taking a long, leisurely path to this point - a good home automation system should make inferences. An inference is a conclusion based on data and reasoning.  So let me be direct - a good home automation system should make inferences.

There.


Oh hell, one more time: a good home automation system should make inferences. That's Point #1.

An inference is a conclusion ("some one is home!") based on data ("the garage door opened, then the house door opened and closed -- all within a few minutes.").

Point #2, we've talked about how the rules that drive the conclusion could change over time. So they shouldn't be hard-coded as some sort of Finite State Machine in the application.

Point #3, tools exist that allow you to draw conclusions based on the appearance of data (in my case, specifically, MQTT event message). 

And these tools allow you to express the event sequence easily.  
At run time.

I know, right?
It's almost as cool as when Smuckers put peanut butter and jelly in the same jar!

Esper estas programaro por okazaĵo rivereto prilaborado.
(I slay myself...)  Esper is an open source, Java based, Event Stream Processor.  Event Stream Processing is a subset/superset/cousin/sibling of Complex Event Processing which we introduced earlier.

You can learn more about Esper from their website: http://www.espertech.com/esper/

Pulled right from their homepage:




Drawing Conclusions in 3, no 5, Easy StePs!


For you Emergenetics Greenies, here's the recipe

  1. Have your Home Automation Sensors publish events on the network. I'm using MQTT
  2. Download and install Esper (You can code Java, right?)
  3. Write Java code
    1. You'll need a main() that instantiates and runs the Esper Engine
    2. You'll need a POJO that represents your Events
    3. Write code to subscribe the the MQTT event stream
    4. Write a handler object, that you want triggered when Esper makes an inference
    5. Write the EPL that represents the event sequence you're after
    6. Tell Esper to associate the handler code and the EPL statement
  4. Run
  5. Debug, lather rinse repeat
See? Five steps.


Esper's a cool tool.  And it comes with (as of this blogging) 733 pages of documentation. Esper's the brain child of a genius, and unfortunately, the documentation assumes you're a genius too.

There are few of the obligatory Hello World examples out there.  Esper comes with plenty of examples, but they're source files.  What mere mortals, like you and me, need are pretty simplistic step-by-step examples.

That's what I'll do next. I'll post my code.
Oh, I use NetBeans. So you JetBrains and Eclipse users will just have to suffer along.





Monday, June 23, 2014

(#11) S.P.O.F. 

Here’s a real life example of how a Home Automation system failure can really stink. Let’s say your carefully crafted Home Automation system is humming away as you leave the house on an extended vacation.  And let’s say you’re heading into the remote wilderness of Southern Utah.


Well, the destination is immaterial, but let’s say you’re heading far, far away from home. 

Oh yes – you've got wireless connectivity. Our example doesn't work if you can’t connect to the ‘net.


For the first four days of your vacation, you check your Home Automation Status page and see that your system is working well.  The doors are all reporting closed, the water leak sensors say they’re dry and the motion sensors see nothing.



Now – the fun part. On day five, at 9:32 pm, your system tells you that the big garage door has just opened. Whoa!  You’re not home, you’re sure of that! And you’re pretty sure no one else has good reason to open the door.


Quick – check the Home Automation Status page.  Yes – the garage door is reporting that it’s Open. It’s saying the Battery is OK and that the sensor is reporting its On-line and functioning.  Check the other sensors: have they entered into the house?  No – the door sensors are closed and have been closed. The motion sensor says No Motion.  Are they rummaging around in the garage?  Maybe the rat-bastards are after my TwentyYearOld, WillNotDie John Deere Weed Whacker!  Or maybe they’re going for the Pizza Hot Pocket snacks in the freezer out there.

Recheck the other sensors – the doors are still showing closed and there’s still no motion.

False Alarm?

But why now?  

The garage door sensor has been working well for weeks.  

Why now?  

Why, at 9:32 pm when I’m 1400 miles from home?


Reliable, Dependable and Trustworthy!
So let’s switch gears and talk about reliability.

Reliability engineering is something I know nothing about. So, like movie critics, I’m allowed to pontificate and make wild assumptions.  I know it’s hard.  Reliability engineering, that is. It’s got to be hard – or Three Mile Island wouldn't have had a problem; Chernobyl wouldn't have blown up; the Therac-25 wouldn't have killed people.

  • How do you make a system reliable?  
  • How do you make it reliable and still affordable?  


Add a second sensor? No – there’s that old proverb: “Man with two watches never knows what time it is.” Add a third sensor?  I've read that’s what they do in Avionics, reliability through redundancy.

Wait – reliability is not at the heart of this system. It’s something else. It’s trust.

In this case, what we've lost is trust. I can’t trust my system anymore.  

Oh -- it works, mostly. 
It works usually. 

And you know what?  

That was the same problem we had with X10 technology.  It worked, usually.



Avionic systems also have enough sensors it’s possible to make deductions on whether a sensor has failed. If one sensor reports something that might claim the plane is flying upside down, yet all of the other sensors indicate normal flight, odds are that we have a sensor failure.


In my house, I can make some deductions.  If an interior door suddenly opens, yet the nearby motion sensor indicates stillness and the exterior doors have remained closed – then odds are it’s a false alarm.  

But not with the garage door sensor.  It’s perfectly reasonable for the door to open at 9:32pm.  Just not when we’re all on vacation.


  • Trust.
  • Trust is earned.
  • Trust is earned over time.  


Once lost, it’ll take a while to earn it back.  We’ll be home from vacation in about two days.  I’ll debug the garage door sensor.  Hopefully the failure will be obvious. Maybe the sensor fell off the door.

Two Days Later -- Epilog

That was it – mechanical failure. The Velcro that held the sensor to the door gave way, the sensor fell off the door and landed in a tilted (open) state.  One #4 wood screw and we’re back in business.

For want of a nail, err, screw...
To borrow the Wisconsin State Motto: foreward!

Tuesday, June 10, 2014

(#10) Raising the Alarm 

If you're a glutton for punishment, or you're doing graduate work in the space, or if you're both a glutton and a grad student, you'll eventually stumble across the concept of event types.  Just Google "event stream vs event cloud" and start reading about event types.

But that's not the point of this post. I've got two types of events coming off the various publishers scattered around the house.  And it comes from the sense that I'll have "status" events and "alarm" events.  And they'll be easily winnowed (or 'sifted') from the event cloud/stream.  

Because I'll want to react to Alarm Events differently from Status Events.

"A subtle thought that is in error..."
The distinction between the two, an alarm and a status event, can be conspicuous or subtle.

  1. "The Door just opened."
  2. "The Door is still open."
  3. "The Door is still open and has been open for two minutes."

By my sophistic thinking, the first one is an Alarm.  The second one a Status Event.

The third - "The door is still open and has been open for two minutes" - could be either.  It depends on whether you consider a Two-Minute-Left-Open Door in your house worrisome or not.



(One of) my point(s) being that whether something is an Alarm or a Status will depend. Depend on a few things.  A few things that will make sense to you and your house and your situation. And your few things that will probably not match my few things.

So?

So don't code this.  This needs to be flexible, changeable, adaptable.


A door open for two minutes in winter's cold December - is an alarm.  A door open for two minutes in Summer might just mean your hands are full taking burgers to the grill.

Wait, no - a door open for two minutes in December in Australia is...


Well, you get the gist.  As programmers we make decisions, lots of them, based on our context. And that leads to inflexibility, complexity and a lack of adoption.


Details, Boy, Details
Ok - so here's an example of two ALARM Events in my system:

WS2308/ALARM | 2014-05-08 20:29:00 -0600 | OUTDOOR HUMIDITY HIGH | 89.0 |

WS2308/ALARM/OUTDOOR/PRESSURE | 2014-05-08 20:29:04 MDT | RAPID OUTDOOR PRESSURE DROP | 0.07 |


Take note of my current, but may change, convention when I publish events

  • The MQTT topic is part of the message payload
  • The MQTT topic starts with the system that created the event -- WS2308 is the name of my weather station
  • The MQTT subtopic is ALARM in this case
  • Vertical bars separate the fields
  • Information is human readable for debugging, instead of in XML or JSON
  • I'm still inconsistent in my MQTT Topic usage. The first alarm could also have been published on the "WS2308/ALARM/OUTDOOR/HUMIDITY" topic.

My weather station publishes weather STATUS events about twice a minute. But it only publishes ALARM events when a threshold has been reached or exceeded.

There's one more thing that I do by convention for my ALARM events, and that is they are published ONCE.

Fire and Forget, Spray and Pray
That's right - my ALARM events are published once.  When a door opens in my house, you'll get one and only one of these:


HHB/ALARM | 2014-05-09 16:19:57 -0600 | 03 | OPEN-CLOSE SENSOR | Front Door | OPEN | 1 | 0000010228 |

I think that's what you want.  I don't think you want your event generator solving for network reliability issues.

And speaking of reliability issues...

Saturday, May 31, 2014

(#9) Complex : adjective kämˈpleks,kəmˈpleks,ˈkämˌpleks/

Our content's about to slow because we're catching up to where I'm still learning myself.

If you're dropping into this blog at this post, let me catch you up: I've asserted that the last 40 years of Home Automation has produced crap and I've take a stab at why I think so. I've told you that I have a couple of systems scattered around my house that broadcast events.

I've told you that it dawned on me that drawing conclusions from (a) the occurrence of an event or (b) the occurrence of a pattern of events is my next task.

CEP - Complex Event Processing

I first heard the term back around 2004, give or take, when I went out to Mountain View, CA to visit some of the engineers at TIBCO. Vivek stopped by for a few minutes and sort of asked if we had heard of this "really cool, new thing." And then he walked us through the basics of using events, specifically patterns of events, to draw conclusions.

If I recall correctly, he told us of a particular North American railroad company that was using it to drive costs out of their infrastructure.


My head jumped to the thought: all that track, all those sensors, all those cars and all that legacy infrastructure. 

If they're into this technology, it has to be approachable.



I won't go too much father into the science, for the most part because I don't know much of the science about this stuff. But I will say if you are interested, start off with two authors who are (or, in my opinion appear to be) the Fathers of CEP: David Luckham and Opher Etzion.

Life Without You
Think, just for a moment, how'd you'd code up something like this.  You'd code an MQTT subscriber, and listen for the first event.  When it came in you'd jump to a state where you'd listen for the second event.  When that one arrived, you'd jump to a state where you'd listen for the 3rd and final event.

Not too bad.

Until you realize that you need to switch things around.  Maybe swap the arrival of events 1 and 3.  Or you want to time-box the whole thing.  Event 3 must come no later than two minutes after event 1. Or maybe you want to add a 4th event.



Yuk! Pretty quickly it should dawn on you that hard-coding this will quickly become frustrating and inflexible.


Wrap it up Already!
Ask and ye shall receive. Here's the solution.

String  anEPLQuery = "SELECT * FROM PATTERN " +
  " [  every eventOne = HHBStatusEvent(macAddress='0000012467',deviceStatus='OPEN') " +
  " -> eventTwo   = HHBStatusEvent(macAddress='000000B357',deviceStatus='OPEN' ) " +
  " -> eventThree = HHBStatusEvent(macAddress='000000B357',deviceStatus='CLOSED' ) ].win:time( 5 minutes )";

Now all you need to know is abit more about the problem!

But this one Java String...
This one simple Java String...

Captures the essence of this requirement.

1) Subscribe to the event stream
2) When you see the following event pattern
2.1) An Open Event from Sensor 0000012467
2.2) Then an Open Event from Sensor 000000B357
2.3) And then a Closed Event from the same Sensor (000000B357)
2.4) And all three events have taken place within a 5 minute sliding window
3) Then do something

It's so smart, it's got to be using something like ESP (Extra Sensory Perception)!
But hold that ESP thought for a moment, I want to take a quick (one post) detour into ALARMS!


Friday, May 30, 2014

(#8) Introducing Homeminder! 

HomeMinder is a programmable electronic system that controls lights, appliances, heating, and cooling in your home through existing house wiring. Visuals on a TV set are used for system set up and operation. It is also possible to control devices by using a smart telephone.

HomeMinder is available in two versions--a free-standing unit that connects to any TV set (or monitor) and a unit built into a TV set. 

The main controller comes packaged with a remote transmitter, one lamp module, one appliance module, and appropriate cables. Also available are extra modules, a light switch module, thermostat controller, and remote control unit.

Is It Worth It?
HomeMinder retails for about $500 with an assortment of seven or eight modules. 


Wow!
Sounds pretty cool.  Not the $500 part. But it sounds like a slick Home Automation System.

I guess it is pretty cool. Or was. In 1984.
Homeminder was "GE Homeminder", a product of a joint venture between Pico/X10 and GE in 1984.

So, it died. 
It went nowhere.
Failure.

(You can read more about Pico/X10 and their products in this article from October 1999).

What's up with all of these failures?
Why have all of these products either shown modest acceptance or made large impact craters?


X10, Insteon, Z-Wave, UPB, Zigbee.
I guess these are as much protocol as product; it's not fair to call a protocol a failure. And there's little value in naming product names.  Folks would simply argue about whether they'd had wide spread acceptance or not.


Big Orange Retail Giants
Or Blue ones - Lowes in this case -- took a run at it last Christmas (2013) with Iris. Read more about Iris here



Was Iris a commercial success? Only Lowes knows. (Hey -- I like that rhyme!)  

But raise your mouse if you know someone who bought one!  

Last time I looked in Lowes, the display had moved from Front and Center to back by the extension cords.  And there was still plenty of product.


I won't ding Iris. 
I have no idea what it can and cannot do.

I'll tell you what I didn't like about it.
  • It was expensive - and subscription based. $10/month.
  • It was closed and proprietary.
  • A motion sensor was $25.
  • A light switch $35.
  • Their thermostat is $100.  I just bought a Nest.


Starter kits were $180 to $240.

I paid $25 for my Eaton Home Heartbeat starter kit.

What if?
What if I buy one and it sucks?  I'm out, what $200? $300?  More?
Will it work with my Nest?  I doubt it.
What if Iris decides to bail on the whole thing, like Eaton did?  What happens to my investment? Does it still work?  When iOS 12 comes out, will their app written for iOS 6 still work?
What if I don't like the way it does something? Can I change it?  Probably not.
Is it open? Hackable? Tinker-able?  Probably not.


These are questions that never, ever cross the mind of someone named Felix in Marketing.
Sorry - he''s just not wired to think these thoughts.
Those neurons don't exist in Felix's brain.


But here's the schnitz Felix - your Home Automation System -- the only ones interested are us engineers.

And all of those "What If's" that you didn't think, we've thunk.
And we walk on by.


Apple
The wild card.  If anyone can pull off a Home Automation System that's closed, proprietary, expensive and limited - it's Apple.  I bet they sell a gazillion of 'em.

Damn Fanbois.


Look at Phillips Hue!  $200 for three colored light bulbs and an iPhone app. I mean, they didn't even toss in a mirrored disco ball! Criminy!