Skip to content

OpenHab Christmas Rule

Dear Reader,

Update: I posted the basic rules over on the OpenHab community site. The result was a much more elegant version of the rule determining if it’s time to turn on the Christmas Lights or not. Check it out and make sure you say thank you to Rich.

If you read my last post “OpenHab Notify Rule” then you know my latest hobby is home automation using OpenHab. it’s fun and while it still involves computers and programming, it helps me remember why I hate Java so much. :)

As I write this, we are in the midst of the Christmas Season and the Lovely and Talented Kathy lives for this time of year so that she can decorate the house. Since we just moved into a new house, this is a “rebuilding year” for us decoration wise, which is the perfect time to begin automating things. Thankfully, OpenHab makes that real easy. Below is my recipe for automating turning things on a dusk and off at 11:00 PM.

 

Items

I don’t have much in the way of Items yet. We just have the lights on the tree to turn on. So here is my single Item that I have to define.

1
2
3
4
5
6
7
Switch Plug_Christmas_Tree "Christmas Tree"  (christmasLights)
{
    channel="zwave:device:512:node29:switch_binary"
}
 
Group christmasLights
Switch sChristmasLights "Christmas Lights" <switch>  [ "Lighting" ]

All I do it turn on and off the plug. That last one, sChristmasLights, is so that I can expose it to Amazon’s Alexa. Don’t judge me! I don’t expose it directly to my network, it goes through a separate system that has a secure tunnel in to this one service. Trust me, I don’t trust Bezos any more than you do.

Rules

The items weren’t the hard part on this particular project, it was the logic behind turning things on and off and when. Here are the constraints:

  • Turn the lights on a Dusk
  • Turn the lights off at 11:00 PM
  • Regardless of what those liars marketers at Wall-Mart, or Target, or Amazon would have you believe, the Christmas season begins on the Friday after Thanksgiving and goes through the end of December. Don’t execute these actions if we are not ‘in season’.

So with those constraints in mind, here is what I came up with.

Imports

Since this is Java, you need to import a few things. These need to be at the top of whatever rules file you put the rules in.

1
2
3
4
5
6
import java.time.Year
import java.time.Month
import java.time.temporal.TemporalAdjusters
import java.time.temporal.ChronoUnit
import java.time.DayOfWeek
import java.time.LocalDate

Turn Lights on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
rule "Christmas Lights ON"
when
  Channel "astro:sun:local:civilDusk#event" triggered START
then
  val String currentYear = String::format("%1$tY", new java.util.Date )
 
  val LocalDate thanksgiving = Year.of(Integer.parseInt(currentYear)).atMonth(Month.NOVEMBER).atDay(1)
    .with(TemporalAdjusters.firstInMonth(DayOfWeek.THURSDAY))
    .with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY))
    .with(TemporalAdjusters.next(DayOfWeek.THURSDAY))
    .with(TemporalAdjusters.next(DayOfWeek.THURSDAY))
    .with(TemporalAdjusters.next(DayOfWeek.THURSDAY))
 
  var LocalDate lastDayOfYear = Year.of(Integer.parseInt(currentYear)).atMonth(Month.DECEMBER).atDay(1)
    .with(TemporalAdjusters.lastDayOfYear)
 
  var LocalDate today = LocalDate.now()
 
  if (
    today.until(thanksgiving,ChronoUnit.DAYS)<=0 &&
    thanksgiving.until(lastDayOfYear, ChronoUnit.DAYS)>0
  ) {
    logInfo("LIGHTS","Light the Christmas tree")
    christmasLights.sendCommand(ON)
  } else {
    logInfo("LIGHTS",today.until(thanksgiving,ChronoUnit.DAYS).toString + " days until the Christmas tree is lit.")
  }
 
end

See, now you too can hate on Java. :)

Honestly, DateTime math sucks in any language and DTM is the heart of this problem. Thanksgiving is the 4th Thursday of the month of November in the US. That big ugly thing starting at line 7 computers the 4th Thursday of the month. On line 5, we computer the current year so that the ugly statement knows which Thanksgiving we want computed.

Now that we know the upper bounds of our window, we need to computer the lower bounds. The last day of the year. That one is easy.

From there it’s easy. Are there zero days or less left until Thanksgiving? If so, have we hit the end of the year? If we are between those two dates, LIGHT THE TREE!

This rule runs at dusk every night and depends on the Astro binding. The Astro was the second binding I installed, Z-Wave being the first. When I write my OpenHab book I think I’ll call it “OpenHab from Astro to Z-Wave” :)

Turn off the lights

Now, I could do all of those calculations every night at 11:00 to see if the Christmas lights need turning off. As it turns out, I’m lazy; that and I discovered the cron rule trigger. To turn the lights off I use the cron trigger to fire my rule every night at 11:00 in November and December. Yes, I cheated, deal with it.

Here’s my lights off rule.

1
2
3
4
5
6
7
rule "Christmas Lights OFF"
when
  Time cron "0 0 23 1/1 NOV,DEC ? *"
then
  christmasLights.sendCommand(OFF)
  logInfo("LIGHTS","Christmas, Lights Off")
end

Simple, huh?

That’s it. With those to rules and three items, I never ever have to worry about turning the tree of or on again. Next year, exterior illumination! :)

Hardware

To make the magic happen, I invested in a GE Z-Wave external plug. While I plan on wiring all my plugs in the house with Z-Wave, I’m not ready to do that just yet. With this, I just plug it in and plug the tree into it. Easy Peasy.

Wrapup

I hope that sharing what I know about seasonal illumination enhances your season. Remember, as much fun as it is to get together with family, that’s not the reason for the season.

Until next time,
I <3 K

=C=

OpenHab: Notify Rule

Dear Reader,

For the past six months I’ve been playing with different home automation solutions. I tried HomeAssistant, VeraPlus, but I kept coming back to OpenHab. OpenHab is not perfect, it’s docs lack and if you are not a programmer it is NOT your solution. (yet)

All that having been said I AM a programmer and OpenHab gives me a lot of flexibility in how things are done, because I can write code in the rules. I am the first to admit that I am an OpenHab noob. the solution I present here might be laughable to those who have been doing it a while. I’m posting it here for two reasons.

  1. As a straw man. If I have it wrong, comment below and help me make it better.
  2. In case it is a good idea and others are wanting to implement it in their OpenHab installation.

For those interested, my setup contains:

Today’s Problem: Notifying People

One of the problems I’ve had to solve recently is that there are times when I want a group of people to be notified when something happens. For different types of events, I want different groups of people notified. The people that need to know a door was opened (mainly just me) is very different from he people I want notified if a door is open and my alarm is set. (EVERYBODY in my family.)

What didn’t work

1
2
3
var contacts = newArrayList()
contacts.add(newLinkedHashMap('name'->'Cal Evans', 'address'->'cal@example.com'))
contacts.add(newLinkedHashMap('name'->'Bob Evans','address'->'bob@example'))

The OpenHab documentation alludes to the fact that you can define variables outside of rules and that any rule in the same file can access those variables. This by itself is true. However, while you can define variables, you cannot manipulate them or execute code outside of a rule.

Originally, I was using an ArrayList of LinkedHashMaps. (Yes, we’ve ventured into Java land) One of my requirements is that it is easy to read and convenient to add new names to the lists. An ArrayList of LinkedHashMaps could be used if I wanted to define everything in a single create statement. Strictly IMHO, this was inelegant and not easy to modify. It was pointed out to me in a thread on the OpenHab community forum titled “Arrays as Globals” that the was not inelegant and was a workable solution. I don’t know enough about Java to be able to define inelegant, all I know was I didn’t like how it looked. So I went looking for another solution.

What didn’t work – Part Deux

Ok, so you can’t manipulate variables outside of a rule in OpenHab. My next thought was to create a System Startup rule that added all the people to my list. the initialization would still be outside of a rule so that it would be global to all rules in that file.

This worked…kind of. Sure enough they names got added, but when I tried to access them I got a Java error:

1
'get' is not a member of 'java.util.LinkedHashMap'

I’m not sure why because get() IS a method of LinkedHashMap according to the documentation.

At this point I had been working on this particular problem for a couple of hours and I had arranged things every way I could think of to no avail. I need to step back and look at the problem from a different angle because this one just wasn’t doing it for me.

What did finally work

Ok, time to re-think this. First, my lack of knowledge of Java and Xtend, the flavor of Java that OpenHab uses, was starting to show. Second, thinking this through I began to realize that variables localized to a given file might get in the way eventually. What I needed was lists in the global namespace that could be accessed from any rule.

So I perverted the String and Group items of OpenHab and forced them to do my bidding.

A String is OpenHab has a label, this is what I used to store my data. I will never use these particular strings to actually display a string like they are supposed to be used, but that’s ok. I decided to store a comma delimited string as the label. This is hard-coded in my “Contacts.items” file so it is permanent.

Next, I needed a way to group my contacts. I have two lists of contacts, normal and emergency contacts. Normal contacts get notified about routine events. (This list is mainly me until I get tired of getting all these emails and eventually texts.) Emergency contacts is everyone who I want to know that my alarm is set and something important happened to trigger that alarm. (Me, the lovely and talented Kathy, the kids, maybe my neighbors, etc.)

My Contacts.items file

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
* Group contact lists
*
* Format for contacts is
* name, METHOD, address
*/
Group normalContacts
Group emergencyContacts
 
String Contact_01 "Cal Evans,TEXT,615-555-1212" (normalContacts, emergencyContacts)
String Contact_02 "Cal Evans,EMAIL,cal@example" (normalContacts, emergencyContacts)
String Contact_03 "Kathy Evans,EMAIL,kathy@example.com" (emergencyContacts)
String Contact_04 "Kathy Evans,TEXT,615-555-1212" (emergencyContacts)

So now we have two globally accessible lists. If you need more info on Strings and Groups, check out the OpenHab Items Doc page. Next I need to use them to notify people that something has happened. So I created a “Notify” rule

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.text.SimpleDateFormat
import java.util.Date
 
/*
* Send a message to the list of people based on the state of Alarm. If alarm
* is set, send to the ALARM list, if Alarm is not set, send to the NonAlarm
* list.
*
* This is called with a comma delimited string. first part is the name of the
* item that triggered the event, the second is the state or message.
*
* Does this need to take Alarm.state into account?
*/
rule "Notify Of Event"
when
Item Notify received command
then
/*
* Build message
*
* The second parameter on some messages will not be a simple state but a
* message. If not ON,OFF,OPEN,CLOSED then just parrot the message sent.
*/
 
/*
* Pick the list to send to
*
* If this is ever used for anything other than notifying people that things
* have opened, list needs to be passed in as a parameter.
*/
var list = switch Security.state.toString {
case "ON" : emergencyContacts
default : normalContacts
}
 
/*
* Build the message
*/
var String subject = receivedCommand.toString.split(",").get(0) + " was " + receivedCommand.toString.split(",").get(1)
var SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm")
var Date eventTime = new Date()
var String message = "At " + dateFormat.format(eventTime) + " " + receivedCommand.toString.split(",").get(0) + " was " + receivedCommand.toString.split(",").get(1)
 
/*
* Send the texts
*/
list.members.filter[ i | i.label.toString.split(",").get(1) == 'EMAIL'].forEach[i | logInfo("TEXT", message) ]
 
/*
* Send the emails
*/
list.members.filter[ i | i.label.toString.split(",").get(1) == 'EMAIL'].forEach[i | sendMail(i.label.toString.split(",").get(2), subject, message) ]
 
end

This code is pretty well documented so I’m not going to walk through it. If you need more info on OpenHab rules syntax, check out the the OpenHab Rules doc page. Once you get used to them, they are really powerful.

Now, anytime I need to notify a group of people that something happened, I send a command to Notify.

1
Notify.sendCommand(receivedCommand.toString.split(',').get(0) + "," + receivedCommand.toString.split(',').get(1))

receivedCommand is an implicit variable in any OpenHab rule. If received command is part of the when clause of the rule then receivedCommand is the command received. In the case of the rule above, the first part is the name of the item that triggered the notify and the second part is that item’s state.

If you are paying attention you probably notices that sending TEXT messages is stubbed out. I need to work out the details but I’m pretty sure it will involve a 3G add-on for my Raspberry Pi and a Twilio SIM card. :) Stay tuned.

fClose()

This solution works. Trust me, it’s been sending me emails every time we open a door to let the dog out. :) It is not perfect though. I think the next iteration, the suggested list will be passed in as a third parameter.  This list will be used unless the Alarm is set and has been triggered. At that point the emergencyContacts list is always used.

If you use OpenHab, I would appreciate comments. How can I make this better? What assumptions did I get wrong, etc.

Oh, I also posted my first post on the OpenHab Community Forums, “Arrays as Globals“. I’m very pleased to say that within minutes I started receiving replies and that they were helpful and courteous. I think I’m going to like the OpenHab community. :)

As much fun as Xtend is however, I am missing PHP for writing rules. The more I work with other languages, the more I miss the simplicity and elegance of PHP.

 

Until next time,
I <3 |<
=C=