Wednesday, October 28, 2015

(#20) Mon Dieu, Part Deux!


A good sequel always recaps how we got here. 

So, we've got a house. We've got sensors in the house. We're trying to be a bit smarter about warning us when the door's been open for a long time. Sometimes we want it left open, sometimes we don't. And, finally, I'm confident that I was not born in a barn.

In this post, I said I could think of two ways to detect if a door's been open too long and I want to know about it.  Here was the first way:

If the door has been open for longer than one minute and the furnace/AC is running then send an alert.  

And here's the other way I might want to figure it out.


IF #2


If the door has been open for longer than one minute and the difference between the inside and outside temperatures is large, then send an alert.

So, in this one, I don't have to wait for the furnace or AC to kick on - I'm going to try to respond before that happens.  If the door is open and it's freezing outside, then I want to know. If the door is open and it's Dante's Seventh Level outside, then I want to know.

While this may appear as overly convoluted, it's also helping me learn more about Esper.




The EPL

In my house the outside temperature comes from the Weather Station, and the first floor inside temperature comes from the Nest Thermostat.  So I've got to join three event streams.

String anEPLQuery = "SELECT * FROM "
+ " HHBStatusEvent.win:time(3 min) as doe, "

+ " NestStatusEvent.win:time(3 min) as nse, "
+ " WS2308WeatherStatusEvent.win:time(3 min) as wse "
+ "WHERE "
+ " ( (doe.deviceType = 3 "              // Open Close sensors are of type 3
+ " AND doe.deviceStatus = 'OPEN' "      // Door is open
+ " AND doe.statusDuration > 60 "        // for more than 60 seconds
+ " AND doe.deviceOnline = TRUE ) "      // Sensor is online
+ " AND (nse.awayStatus = 'HOME') "      // Nest thinks we're home


// and the difference between inside and outside is more that 10 degrees 

+ " AND (((wse.oTemp - nse.temperature ) < -10) OR"     
+ "      ((wse.oTemp - nse.temperature ) > 10) ) )";



 

The changes from our first solution are in red.

We're joining a third event stream, status events from our weather station.  We're checking to make sure that Nest thinks we're Home (don't ask why) and we're comparing the inside and outside temperatures.

Anything more that an 10 degree (F) difference triggers the result. (I didn't see an Absolute Value intrinsic function in EPL. I did see a post about invoking Java's Math.Abs function but also a note that it was having an issue. So a simple pair of subtractions seemed easiest.


And to cut to the chase - this works too.

So let's recap what else we need to do to get things moving.  Jump back to this post on Esper if you need a quick refresher on what it takes to get Esper up and processing events.


The Listener

In Esper parlance, a Listener is the class that gets invoked when a match is found; when the EPL finds a set of events that match what you're looking for.

In our EPL, we've asked Esper to send us the Events (as POJOs) that triggered the match (that's what SELECT * does).

So step one on a Listener is to have it implement the UpdateListener interface and override the update method. Like this:

public class LeftDoorOpenListener implements UpdateListener
{
    @Override
    public void update(EventBean[] newData, EventBean[] oldData)
    {

...





      
The update method will get called when a stream of matching events is detected. So, as a first step, let's extract the events that triggered the update:

NestStatusEvent nse = (NestStatusEvent) newData[ 0 ].get( "nse" );
 

HHBStatusEvent hse = (HHBStatusEvent) newData[ 0 ].get( "doe" );

WS2308WeatherStatusEvent  

    wse = (WS2308WeatherStatusEvent) newData[ 0 ].get( "wse" );

We pull the events out using the names we gave them in the EPL statement above.

Next - well, next is up to you.  You've got the three events that triggered the EPL so you can do whatever you'd like to do.  You can do additional processing or checking or throttling in the Listener.




In my case, since I'm an event driven house, I create a new event and plunk it back onto the MQTT bus:

StringBuffer    sb = new StringBuffer();
sb.append( "CEP/CONCLUSION" );              sb.append( " | " );
sb.append( getCurrentDateTime() );          sb.append( " | " );
sb.append( "DOOR LEFT OPEN" );              sb.append( " | " );
sb.append( hse.getDeviceName() );           sb.append( " | " );
sb.append( hse.getStatusDuration() );       sb.append( " | " );
sb.append( nse.getHvacStatus() );           sb.append( " | " );
sb.append( nse.getFanStatus() );            sb.append( " | " );
sb.append( nse.getAwayStatus() );           sb.append( " | " );
sb.append( wse.getoTemp() );                sb.append( " | " );



Which will result in a new MQTT event like this:

CEP/CONCLUSION | 2015-10-28 18:52:10 -0600 | DOOR LEFT OPEN | Front Door | 81 | HEATING | FAN ON | HOME | 49.0 | 

Breaking it down:
MQTT Topic:          CEP/CONCLUSION
Date/Time:           2015-10-28 18:52:10 -0600
Message:             DOOR LEFT OPEN
Device Name:         Front Door
Num Seconds:         81
Furnace/AC:          HEATING
Fan is:              FAN ON 
Home or Away:        HOME
Outside temperature: 49.0


Next up - let's create some more test data and try out a few scenarios.  I think the EPL will need tweaking of the views.

No comments :

Post a Comment