Setup openHAB

This is the second in a series of blog posts about some of my adventures into the world of Home Automation with openHAB. Part 1 was primarily about building and preparing the Linux (Debian) based system and the openHAB base install. In this second article we will dive into the details of maken openHAB do usefull things, like controlling lights or showing gas and electricity usage data and graphs. Note that this set of articles is focused on my particular setup. Your milage may vary, but you will most likely pick-up a thing or two for your own environment.

In part 1, we installed Debian Linux and various tools that support openHAB like a graphing tool Grafana and time series database (InfluxDB) for storing persistent history of item states (timeseries data), and openHAB itself.

Through addons we will make openHAB do more than just consuming some memory and CPU on the server... :o)

In these blog posts, I will not explain the concepts of openHAB and it's 'data model'. It is assumed that you are somewhat familiar with openHAB concepts like Items, Things and Channels. For background information on openHAB, please read the official introductory documentation.

OpenHAB add-ons

To make openHAB actually do something, bindings and other add-ons must be installed. Since we installed openHAB manually, out of the box it is very 'basic' but that gives us full control of the configuration, and we like control, don't we? We must define and install the required UI's to interact with openHAB. This is specified in the configuration file $OPENHAB_CONF/services/addons.cfg. The initial addons.cfg file looks like this:

# The installation package of this openHAB instance
# Note: This is only regarded at the VERY FIRST START of openHAB
# Note: If you want to specify your add-ons yourself through entries below, set the package to "minimal"
# as otherwise your definition might be in conflict with what the installation package defines.
package = minimal

# Access Remote Add-on Repository
remote = true

# Include legacy 1.x bindings for which there is already a 2.x version available.
legacy = false

# A comma-separated list of bindings to install (e.g. "binding = sonos,knx,zwave")
binding = astro,ntp,mqtt,mqttgeneric,hue

# A comma-separated list of UIs to install (e.g. "ui = basic,paper,habpanel")
ui = basic,paper

# A comma-separated list of persistence services to install (e.g. "persistence = rrd4j,jpa")
persistence = influxdb,mapdb

# A comma-separated list of actions to install (e.g. "action = mail,pushover")
#action = pushsafer,xmbc

# List of transformation services to install (e.g. "transformation = map,exec,jsonpath")
transformation = jsonpath,map

# A comma-separated list of voice services to install (e.g. "voice = marytts,freetts")
#voice = googletts

# A comma-separated list of miscellaneous services to install (e.g. "misc = myopenhab")
#misc = restdocs

Walking through the most relevant settings one by one. First package = minimal which ensures that everything that is installed is because we specified it explicitly, otherwise openHAB will install a bunch of default stuff.

With the legacy=false option we block old openHAB 1.x bindings for which there is already a 2.x version available. Since I am not migrating from a 1.x version of openHAB, there is no need for these legacy bindings.

The ui = basic,paper installs two web user interfaces: Paper UI that can be used for configuration or checking binding parameters, and Basic UI which is the primary user interface we will use to access and control all the openHAB stuff for now.

By default openHAB has no persistence for historic states of items and things. With the parameter persistence = influxdb,mapdb we define two different persistence services. mapdb is used for persisting state through reboot/restart. This persistence service stores the last state of specified items, otherwise openHAB would not know the actual state of devices after a reboot. For long term historic data and graphing we use InfluxDB which is optimized for storing and retrieving timeseries data like sensor states.

Transformations are primarily used to transform a value into something else, including translation to other language, from number to text (e.g. 0=OFF, 1=ON) or more complex stuff with JSON or JavaScript. For now we just install two transformations with transformation = jsonpath,map.

At the core of openHAB are the bindings. They implement the devices to control with Home Automation and provide the specific protocols and commands to manage these devices, including Philips Hue lights, MQTT connections, etc. As a first step we will only install a few bindings with binding = atro,ntp,mqtt,mqttgeneric,hue. Others will follow later.

After changing the configuration file with Visual Studio Code, openHAB will automatically pick up the changes.

Setup persistence services

We've already installed InfluxDB and the accompanying openHAB persistence service on the server in part 1. To connect to InfluxDB from openHAB we need to define the connection details in $OPENHAB_CONF/services/influxdb.cfg.

# The database URL, e.g. http://127.0.0.1:8086 or https://127.0.0.1:8084
url=http://localhost:8886

# The name of the database user, e.g. openhab.
user=<USERNAME>

# The password of the database user.
password=<PASSWORD>

# The name of the database, e.g. openhab.
db=openhab_db

The InfluxDB persistence service is defined as default persistence service if nothing else is defined in openHAB. We'll use the InfluxDB service for storing historical data we can use to create graphic overviews over the last week, month, year, etc. We need an influxdb.persist file to define what type of data must be persisted and at what frequency. The easiest way to accomplish that is by defining a group in the persistence file and every item you want to persist can be made member of that group. That's simple enough. So let's look at the $OPENHAB_CONF/persistence/influxdb.persist file.

Strategies {
    everyMinute: "0 * * * * ?"
    every5Minutes: "0 0/5 * * * ?"
    every10Minutes: "0 0/10 * * * ?"
    every15Minutes: "0 0/15 * * * ?"
    everyHour: "0 0 * * * ?"
    everyDay: "0 1 0 * * ?"
    everyMonth: "0 1 0 1 1/1 ?"
    default = everyChange
}
Items {
    gHistory* : strategy = everyChange, everyMinute
    gHourly* : strategy = everyChange, everyHour
    gDaily* : strategy = everyChange, everyDay
    gMonthly* : strategy = everyChange, everyMonth
    gChart* : strategy = everyChange, every5Minutes
}

Changes to any Item that is a member of the gHistory group will be stored in InfluxDB as timeseries data, at least once a minute, which can be used in queries to create graphic overviews. For example to keep track of actual power consumption as reported by my energy meter, the Item definition would look like:

Number Pwr_Use "Current consumption [%d W]" <energy> (gHistory) {channel="..."}

From now on, we'll add (gHistory) or one of the other persistence groups to every Item we want to persist for historic data. For more background on persistence, read this post in the openHAB community forum. Of course we must create the Groups for persistence services in $OPENHAB_CONF/items/persistence.items:

//==============================================================================
// Persistence Groups
//==============================================================================
//------------------------------------------------------------------------------
// Define Groups to assign Items to specific persistence services:
//  mapdb for openHAB state restore on restart
//  influxdb for charts and history
//------------------------------------------------------------------------------
// Group to persist Items for etailed history and Grafana views (using InfluxDB)
Group gHistory
// Group to persist Items for long-term history and Grafan views
Group gChart    
// Group to persist Items to restore on startup using mapdb
Group gRestore

For the mapdb service we have no need to change the service configuration, although there is a $OPENHAB_CONF/services/mapdb.cfg created automatically when installing the addon. All we have to do is tell openHAB to persist all items in the mapdb database (a simple key-value store) on every update, and restore their values on reboot. This can be done with a persistence definition in $OPENHAB_CONF/persistence/mapdb.persist:

Strategies {
    default = everyUpdate
}
Items {
    // persist all items on every change and restore them from the db at startup
    * : strategy = everyChange, restoreOnStartup
}

Reflect your house in the Groups

Groups are an important concept in OH. They allow you to act upon multiple items, create views, and assign persistence options, to name a few. So, our first order of business is to create Groups for the different areas in and around the house. Create a file $OPENHAB_CONF/items/openhab.items and define some groups there. More groups will follow later.

//==============================================================================
// Generic groups for all Items
//==============================================================================

// Groups for house environment
Group gGroundFloor  "Ground Floor"  <groundfloor>
Group gUpperFloor   "Upper Floor"   <firstfloor>
Group gOutdoor      "Outdoor"       <garden>

//Groups for sensors
Group gClimate      "Climate"

//Groups for outdoor
Group gOutside      "Outside"       <outdoorlight>      (gOutdoor)
Group gGarden       "Garden"        <garden>            (gOutdoor)
// Group for all Livingroom items
Group gLiving       "Livingroom"    <sofa>              (gGroundFloor)
// Group for all Guestroom items
Group gGuest        "Guestroom"     <suitcase>          (gUpperFloor)
// Group for all Laundry items
Group gLaundry      "Laundry"                           (gUpperFloor)
// Group for all Bathroom items
Group gBathroom     "Bathroom"      <bath>              (gUpperFloor)
// Group for all Bedroom items
Group gBedroom      "Bedroom"       <bedroom>           (gUpperFloor)
//Group for all Kitchen items
Group gKitchen      "Kitchen"       <kitchen>           (gGroundFloor)
//Group for all Dining room items
Group gDining       "Dining"                            (gGroundFloor)
//Group for all Hallway items
Group gHall         "Hallway"       <wardrobe>          (gGroundFloor)
// Group for all Corridor items
Group gCorridor     "Corridor"      <corridor>          (gUpperFloor)
//Group for all Study/work room items
Group gStudy        "Study"         <office>            (gUpperFloor)
//Group for all Toilet items
Group gToilet       "Toilet"        <toilet>            (gGroundFloor)
//Group for all Scullery items
Group gScullery     "Scullery"      <washingmachine>    (gGroundFloor)
// Group for all Garaga items
Group gGarage       "Garage"        <garage>            (gGroundFloor)
// Group for all Lodge items
Group gLodge        "Lodge"         <terrace>           (gOutdoor)
// Group for all Toolshed items
Group gTools        "Toolshed"                          (gOutdoor)

//Group for all Temperature sensors
Group gTemp         "Temperature"   <temperature>       (gClimate)

I define some default icons to use when displaying a group (between '<>'). Notice there is no proper icon in the standard set of 'classic icons' for a diningroom or toolshed. The icons available in OH can be found here. You can add your own icons; more on that later.

Your first bindings

As said, the functionality of openHAB is primarly activated through the installation of bindings for devices and sensors. Since we've already installed a few bindings, let's configure them now.

In these articles I use configuration files to 'manually' setup the configuration. In OH2 there is also a discocvery service accessed via the Paper UI that can do most, but not all, of the configuration stuff required.

Tell time: the NTP and Astro bindings

Keeping time is essential for humans and automation systems alike. We will use the NTP binding to sync with formal time servers on the Internet that support the NTP protocol. The binding needs a simple Things configuration, specifying the time servers to sync with as well as the refresh rate.

ntp:ntp:local  [ hostname="nl.pool.ntp.org", refreshInterval=60, refreshNtp=30 ]

OpenHAB will load the binding in a few seconds as can be witnessed in the $OPENHAB_USERDATA/logs/events.log:

[hingStatusInfoChangedEvent] - 'ntp:ntp:local' changed from UNINITIALIZED to INITIALIZING
[hingStatusInfoChangedEvent] - 'ntp:ntp:local' changed from INITIALIZING to ONLINE

The next binding, the Astro binding, gives us all kinds of information related to date and time and in particular to Sun and Moon position details, like sunset and sunrise, full moon, day phase, etc. Very usefull for automation based on time of day or sun position for instance.

The Thing definition for the Astro binding only needs your home location, like this (obfuscated for privacy/security reasons):

astro:sun:home [ geolocation="5x.xxxxxx,4.xxxxxx", interval=300]
astro:moon:home [ geolocation="5x.xxxxxx,4.xxxxxx", interval=300]

In case you don't know the Latitude and Longitude of your home, Google Maps can help out: Drop a pin on your location in Google Maps, right-click on it and select What is here?. A small window will appear with the exact coordinates. Copy them into the Things file (I use 6 decimals for precision, tghat should be more than adequate). The Binding will load as can be seen in the $OPENHAB_USERDATA/logs/openhab.log file,

[el.core.internal.ModelRepositoryImpl] - Loading model 'astro.things'
[thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:home
[thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:moon:home
[el.core.internal.ModelRepositoryImpl] - Refreshing model 'astro.things'

And the $OPENHAB_USERDATA/logs/events.log file shows it is initializing properly.

[hingStatusInfoChangedEvent] - 'astro:sun:home' changed from UNINITIALIZED to INITIALIZING
[hingStatusInfoChangedEvent] - 'astro:sun:home' changed from INITIALIZING to ONLINE
[hingStatusInfoChangedEvent] - 'astro:moon:home' changed from UNINITIALIZED to INITIALIZING
[hingStatusInfoChangedEvent] - 'astro:moon:home' changed from INITIALIZING to ONLINE

The Astro binding exposes quite a lot of channels; I'm only interested in a subset of them as shown in the Items file below. Enter it in $OPENHAB_CONF/items/astro.items.

//==============================================================================
// Items for the Astro binding providing sun, moon, time of day related info.
//==============================================================================

// Group for all Astro Items
Group gAstro <sun_clouds>

//------------------------------------------------------------------------------
// Date and time related Items
//------------------------------------------------------------------------------

// The NTP-synced local date and time
DateTime Current_DateTime "Today [%1$tA, %1$td.%1$tm.%1$tY]"        <clock>         (gAstro)    {channel="ntp:ntp:local:dateTime"}
// Current day phase (SUN_RISE, ASTRO_DAWN, NAUTIC_DAWN, CIVIL_DAWN, CIVIL_DUSK, NAUTIC_DUSK, ASTRO_DUSK, SUN_SET, DAYLIGHT, NIGHT)
// translated to local text via transformation
String          Day_Phase        "Day phase [MAP(astro.map):%s]"                    (gAstro)    {channel="astro:sun:home:phase#name"}
Switch          Night_State      "Night"                                            (gAstro,gRestore)    // Set via rule
// Current season name, translated to local text
String          Season_Name      "Season [MAP(astro.map):%s]"                       (gAstro)    {channel="astro:sun:home:season#name"}

//------------------------------------------------------------------------------
// Sunrise and sunset related Items
//------------------------------------------------------------------------------

// Sun rise/set parameters
DateTime        Sunset_Time     "Sunset [%1$tH:%1$tM]"              <sunset>        (gAstro)            {channel="astro:sun:home:set#start"}
DateTime        Sunrise_Time    "Sunrise [%1$tH:%1$tM]"             <sunrise>       (gAstro)            {channel="astro:sun:home:rise#end"}
DateTime        Noon_Time       "Noon [%1$tH:%1$tM]"                                (gAstro)            {channel="astro:sun:home:noon#start"}
DateTime        Night_Time      "Night [%1$tH:%1$tM]"                               (gAstro)            {channel="astro:sun:home:night#start"}
DateTime        Daylight_Time   "Daylight [%1$tH:%1$tM]"                            (gAstro)            {channel="astro:sun:home:daylight#start"}
DateTime        Evening_Time    "Evening [%1$tH:%1$tM]"                             (gAstro)            {channel="astro:sun:home:eveningNight#start"}
DateTime        Morning_Time    "Morning [%1$tH:%1$tM]"                             (gAstro)            {channel="astro:sun:home:morningNight#start"}
// Sun position parameters
Number:Angle    Sun_Elevation   "Sun elevation [%.0f %unit%]"       <sun>           (gAstro)            {channel="astro:sun:home:position#elevation"}
Number          Sun_Azimuth     "Sun azimut [%.0f °]"               <sun>           (gAstro)            {channel="astro:sun:home:position#azimuth"}
// Sun direction is 'calculated' from azimuth
String          Sun_Direction   "Sun angle [%s]"                    <sun>           (gAstro)            {channel="astro:sun:home:position#azimuth"}
// We will use this to show when the sun is down
Switch          Sun_Down        "Sun down proxy switch"             <sun>           (gAstro)
//
Switch          NightState

//------------------------------------------------------------------------------
// Moon related Items
//------------------------------------------------------------------------------
Number          Moon_Distance   "Moon Distance [%.0f km]"           <moon>          (gAstro)            {channel="astro:moon:home:distance#distance"}
String          Moon_Phase      "Moon Phase [MAP(astro.map):%s]"    <moon>          (gAstro)            {channel="astro:moon:home:phase#name"}
//------------------------------------------------------------------------------
// Radiation related Items
//------------------------------------------------------------------------------

Number:Intensity Tot_Radiation  "Radiation [%.2f %unit%]"                           (gAstro,gHistory)   {channel="astro:sun:home:radiation#total"}
Number:Intensity Dir_Radiation  "Direct Radiation [%.2f %unit%]"                    (gAstro,gHistory)   {channel="astro:sun:home:radiation#direct"}
Number:Intensity Diff_Radiation "Diffuse Radiation [%.2f %unit%]"                   (gAstro,gHistory)   {channel="astro:sun:home:radiation#diffuse"}

The Astro Items for the various start/end times of day phases have a specific relationship as detailed in the table below.

Astro channel Description Relationship
set#start Sun set starts sun is at horizon
set#end Sun set completed 4 minutes after set#start
rise#start Sun rise starts Sun is just below horizon
rise#end Sun rise ends 4 minutes after rise#start
noon#start Sun at highest posititon noon#end is 1 min later
noon#end Same non#start is 1 min earlier
night#start Two hours after end of sunset set#end is 2 hr earlier
night#end Two hours before start of sunrise rise#start is 2 hr later

We use the map transformation function to translate astro status to local language text using $OPENHAB_CONF/transformation/astro.map, based on this openHAB community post, and in my case translated to Dutch.

// Zodiac
ARIES=♈ Ram
TAURUS=♉ Stier
GEMINI=♊ Tweeling
CANCER=♋ Kreeft
LEO=♌ Leeuw
VIRGO=♍ Maagd
LIBRA=♎ Weegschaal
SCORPIO=♏ Schorpioen
SAGITTARIUS=♐ Boogschutter
CAPRICORN=♑ Steenbok
AQUARIUS=♒ Waterman
PISCES=♓ Vissen
// Seasons
SPRING=Lente
SUMMER=Zomer
AUTUMN=Herfst
WINTER=Winter
// Day phases
SUN_RISE=Zonsopkomst
ASTRO_DAWN=Astronomische morgenschemering
NAUTIC_DAWN=Nautische morgenschemering
CIVIL_DAWN=Burgerlijke morgenschemering
CIVIL_DUSK=Burgerlijke avondschemering
NAUTIC_DUSK=Nautische avondschemering
ASTRO_DUSK=Astronomische avondschemering
SUN_SET=Zonsondergang
DAYLIGHT=Dag
//NOON=Middag
NIGHT=Nacht
// Moon states
NEW=🌑 Nieuwe maan
WAXING_CRESCENT=🌑→🌓 wassende halve maan
FIRST_QUARTER=🌓 eerste kwartier
WAXING_GIBBOUS=🌓→🌕 wassende maan
FULL=🌕 Volle maan
WANING_GIBBOUS=🌕→🌗 afnemende maan
THIRD_QUARTER=🌗 laatse kwartier
WANING_CRESCENT=🌗→🌑 afnemende halve maan
// Unknown
NULL=onbekend ⁉
UNDEF=onbekend ⁉
-=onbekend ⁉

Save the Things, Map and Items file and check the log for possible errors. Judging by the entries in my log file, we're in (Astro) business:

[.ItemChannelLinkAddedEvent] - Link 'Current_DateTime-ntp:ntp:local:dateTime' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Day_Phase-astro:sun:home:phase#name' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Season_Name-astro:sun:home:season#name' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Sunset_Time-astro:sun:home:set#start' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Sunrise_Time-astro:sun:home:rise#end' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Noon_Time-astro:sun:home:noon#start' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Night_Time-astro:sun:home:night#start' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Daylight_Time-astro:sun:home:daylight#start' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Evening_Time-astro:sun:home:eveningNight#start' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Morning_Time-astro:sun:home:morningNight#start' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Sun_Elevation-astro:sun:home:position#elevation' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Sun_Azimuth-astro:sun:home:position#azimuth' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Total_Radiation-astro:sun:home:radiation#total' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Direct_Radiation-astro:sun:home:radiation#direct' has been added.
[.ItemChannelLinkAddedEvent] - Link 'Diffuse_Radiation-astro:sun:home:radiation#diffuse' has been added.
[vent.ItemStateChangedEvent] - Current_DateTime changed from NULL to 2018-12-22T15:41:49.317+0100
[vent.ItemStateChangedEvent] - Day_Phase changed from NULL to DAYLIGHT
[vent.ItemStateChangedEvent] - Season_Name changed from NULL to AUTUMN
[vent.ItemStateChangedEvent] - Sunset_Time changed from NULL to 2018-12-22T16:44:00.000+0100
[vent.ItemStateChangedEvent] - Night_Time changed from NULL to 2018-12-22T18:48:00.000+0100
[vent.ItemStateChangedEvent] - Daylight_Time changed from NULL to 2018-12-22T08:17:00.000+0100
[vent.ItemStateChangedEvent] - Noon_Time changed from NULL to 2018-12-22T12:30:00.000+0100
[vent.ItemStateChangedEvent] - Evening_Time changed from NULL to 2018-12-22T18:48:00.000+0100
[vent.ItemStateChangedEvent] - Morning_Time changed from NULL to 2018-12-22T00:00:00.000+0100
[vent.ItemStateChangedEvent] - Sunrise_Time changed from NULL to 2018-12-22T08:17:00.000+0100
[vent.ItemStateChangedEvent] - Total_Radiation changed from NULL to 67.53952970443542 W/m²
[vent.ItemStateChangedEvent] - Sun_Azimuth changed from NULL to 220.61924992671453
[vent.ItemStateChangedEvent] - Direct_Radiation changed from NULL to 9.935978720116415 W/m²
[vent.ItemStateChangedEvent] - Diffuse_Radiation changed from NULL to 57.60355098431901 W/m²
[vent.ItemStateChangedEvent] - Sun_Azimuth changed from 220.61924992671453 to 224.9026314329137
[vent.ItemStateChangedEvent] - Sun_Elevation changed from NULL to 7.055156617674935 °
[vent.ItemStateChangedEvent] - Direct_Radiation changed from 9.935978720116415 W/m² to 3.286331269237091 W/m²
[vent.ItemStateChangedEvent] - Diffuse_Radiation changed from 57.60355098431901 W/m² to 45.70268560767809 W/m²
[vent.ItemStateChangedEvent] - Total_Radiation changed from 67.53952970443542 W/m² to 48.98901687691518 W/m²
[vent.ItemStateChangedEvent] - Sun_Azimuth changed from 224.9026314329137 to 224.90285547818374
[vent.ItemStateChangedEvent] - Sun_Elevation changed from 7.055156617674935 ° to 7.055040379577557 °
[vent.ItemStateChangedEvent] - Direct_Radiation changed from 3.286331269237091 W/m² to 3.2860840096064012 W/m²

We can use the Astro Items to determine time of day, sun position, zodiac, season and other stuff to automate things based on this. I like to show a readable display of the current sun position (like East, SouthEast, etc.) instead of angle. For that we create a Rule to 'calculate' the textual representation of the sun angle. As this is the first time we are doing something with rules, let's look at some of the basics of rules. Rules are defined in the $OPENHAB_CONF/rules directory in files with extension .rules. It's good practice to separate rules related to different bindings or topics in different files. The structure of a rule is quite simple:

rule "<RULE NAME>"
when
   <CONDITION> [or <CONDITION> ...]
then
    <ACTIONS>
end

A rule file can contain multiple rules and they can become quite complex at times. More information, like the syntax of the rules, can be found here. One of the biggest challenge people face when beginning to create rules is the proper handling of Items and values. Simply put: make sure you use the state of an Item when doing calculations or comparision and secondly be aware of (undefined) NULL values, especially since rules are executed asynchronously (by default in up to 5 threads) related to other OH processes. Especially in the program startup phase this can play havoc with your rules; it could very well be that not all Items are loaded/defined when starting a rule, for instance.

A simple rule I use to translate the sun angle to a human readable position (there may be beter to do this):

rule "Azimut to Wind direrctions"
when
    Item Sun_Azimuth changed
then
    if (Sun_Azimuth.state <12)
        Sun_Direction.postUpdate("Noord")
    else if (Sun_Azimuth.state <34)
        Sun_Direction.postUpdate("NoordNoordOost")
    else if (Sun_Azimuth.state < 57)
        Sun_Direction.postUpdate("NoordOost")
    else if (Sun_Azimuth.state < 79)
        Sun_Direction.postUpdate("OostNoordOost")
    else if (Sun_Azimuth.state < 102)
        Sun_Direction.postUpdate("Oost")
    else if (Sun_Azimuth.state < 125)
        Sun_Direction.postUpdate("OostZuidOost")
    else if (Sun_Azimuth.state < 147)
        Sun_Direction.postUpdate("ZuidOost")
    else if (Sun_Azimuth.state < 170)
        Sun_Direction.postUpdate("ZuidZuidOost")
    else if (Sun_Azimuth.state < 192)
        Sun_Direction.postUpdate("Zuid")
    else if (Sun_Azimuth.state < 215)
        Sun_Direction.postUpdate("ZuidZuidWest")
    else if (Sun_Azimuth.state < 237)
        Sun_Direction.postUpdate("ZuidWest")
    else if (Sun_Azimuth.state < 259)
        Sun_Direction.postUpdate("WestZuidWest")
    else if (Sun_Azimuth.state < 283)
        Sun_Direction.postUpdate("West")
    else if (Sun_Azimuth.state < 305)
        Sun_Direction.postUpdate("WestNoordWest")
    else if (Sun_Azimuth.state < 327)
        Sun_Direction.postUpdate("NoordWest")
    else if (Sun_Azimuth.state < 349)
        Sun_Direction.postUpdate("NoordNoordWest")
    else
        Sun_Direction.postUpdate("Noord")
end

The MQTT binding

The MQTT binding of openHAB has changed drastically in openHAB 2.4.0 and the old version is referenced as mqtt1. We already installed the bindings mqtt and mqttgeneric in the previous step. Now we must set the parameters to connect to the MQTT broker Mosquitto in the binding definition.

Edit the file /opt/openhab2/conf/things/mqtt.things.

Bridge mqtt:broker:mosquitto [ host="192.168.1.2", port="1883", secure="AUTO", username="<USERNAME>", password="<PASSWORD>" ]
{
    //Things definition goes here
}

Use the username and password as specified during MQTT server setup before. The specified port is the default port for Mosquitto, change if you selected another port during Mosquitto setup before.

One of the devices that communicate through MQTT in my setup is the smart energy meter that uses the DSMR standard. I've created a little ESP8266 based device that reads the P1 serial port and publishes the relevant data as a JSON formatted MQTT message. To test the DSMR-to-MQTT gateway I subscribe to the sensor/dsmr topic.

$ mosquitto_sub -h localhost -t sensor/dsmr -u "<USERNAME>" -P "<PASSWORD>"

The messages come in about every 10 seconds like this (DSMR v4 standard dictates a 'telegram' every 10 seconds):

{"dsmr":"42","power":{"time":"180925074337S","tariff":"2","use":{"total":{"T1":"11529549","T2":"10320634"},"actual":{"total":"568","L1":"107","L2" :"225","L3":"236"}},"return":{"total":{"T1":"0","T2":"0"},"actual":{"total":"0"}}},"gas":{"time":"180925070000S)","use":"4891357"}}
{"dsmr":"42","power":{"time":"180925074347S","tariff":"2","use":{"total":{"T1":"11529549","T2":"10320636"},"actual":{"total":"547","L1":"107","L2":"223","L3":"216"}},"return":{"total":{"T1":"0","T2":"0"},"actual":{"total":"0"}}},"gas":{"time":"180925070000S)","use":"4891357"}}
{"dsmr":"42","power":{"time":"180925074357S","tariff":"2","use":{"total":{"T1":"11529549","T2":"10320644"},"actual":{"total":"2815","L1":"95","L2":"228","L3":"2491"}},"return":{"total":{"T1":"0","T2":"0"},"actual":{"total":"0"}}},"gas":{"time":"180925070000S)","use":"4891357"}}

The DSMR message format in JSON-speak looks like this:

{
  "dsmr":"<version>",
  "power":
  {
    "tariff":"<1|2>",
    "use":
    { "total": { "T1": "<power-kWh>", "T2": "<power-kWh>" },
      "actual": { "total": "<power-kW>", "L1": "<power-kW>", "L2": "<power-kW>", "L3": "<power-kW>" }
    },
    "return":
    { "total": { "T1": "<power-kWh>", "T2": "<power-kWh>" },
      "actual": { "total": "<power-kW>", "L1": "<power-kW>", "L2": "<power-kW>", "L3": "<power-kW>" }
    }
  },
  "gas": { "total": "<gasmeter-m3>" }
}

Our first MQTT connection: smart meter data

For the Smart Meter (DSMR) that generates power and gas usage data we need to define the Channels in the mqtt.things file. For each meter reading a separate channel.

Bridge mqtt:broker:mosquitto [ host="192.168.1.2", port="1883", secure="AUTO", username="<USERNAME>", password="<PASSWORD>" ]
{
    Thing topic dsmr "DSMR reader" {
    Channels:
        Type number : P_Use         "Current consumption"   [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.use.actual.total" ]
        Type number : P_UseL1       "L1 consumption"        [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.use.actual.L1" ]
        Type number : P_UseL2       "L2 consumption"        [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.use.actual.L2" ]
        Type number : P_UseL3       "L3 consumption"        [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.use.actual.L3" ]
        Type number : P_Ret         "Current return"        [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.return.actual.total" ]
        Type number : P_RetL1       "L1 return"             [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.return.actual.L1" ]
        Type number : P_RetL2       "L2 return"             [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.return.actual.L2" ]
        Type number : P_RetL3       "L3 return"             [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.return.actual.L3" ]
        Type number : P_Tariff      "Tariff"                [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.tariff" ]
        Type number : P_UseTotT1    "T1 consumption"        [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.use.total.T1" ]
        Type number : P_UseTotT2    "T2 consumption"        [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.use.total.T2" ]
        Type number : P_RetTotT1    "T1 return"             [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.return.total.T1" ]
        Type number : P_RetTotT2    "T2 return"             [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.power.return.total.T2" ]
        Type number : G_UseTot      "Gas consumption"       [ stateTopic="sensor/dsmr", transformationPattern="JSONPATH:$.gas.total" ]
    }
}

Next we must define Items to store the values received via MQTT messages. The corresponding Item definitions are stored in $OPENHAB_CONF/items/dsmr.items. Note that the topic field in the channel definitions below refers to topic in the Thing definition above: "Thing topic dsmr ...", and mosquitto in the channel definitions below refers to the corresponding field in the Bridge definition above: "Bridge mqtt:broker:mosquitto ..."

Group gEnergy <energy>

// Items to store current actual power consumption readings from DSMR (received through MQTT message).
Number          Pwr_Use         "Huidig verbruik [%.3f kW]"     <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_Use"}
Number          Pwr_UseL1       "Verbruik L1 [%.3f kW]"         <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_UseL1"}
Number          Pwr_UseL2       "Verbruik L2 [%.3f kW]"         <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_UseL2"}
Number          Pwr_UseL3       "Verbruik L3 [%.3f kW]"         <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_UseL3"}

// Items to store current actual power return readings from DSMR (received through MQTT message).
Number          Pwr_Ret         "Huidig retour [%.3f kW]"       <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_Ret"}
Number          Pwr_RetL1       "Retour L1 [%.3f kW]"           <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_RetL1"}
Number          Pwr_RetL2       "Retour L2 [%.3f kW]"           <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_RetL2"}
Number          Pwr_RetL3       "Retour L3 [%.3f kW]"           <energy>    (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_RetL3"}

// Items to store meter readings of total consumption since install time of the DSMR (received through MQTT message).
Number         Pwr_UseTotT1    "Meterstand Dal [%d kWh]"       <energy>    (gHistory,gEnergy,gRestore)  {channel="mqtt:topic:mosquitto:dsmr:P_UseTotT1"}
Number         Pwr_UseTotT2    "Meterstand Hoog [%d kWh]"      <energy>    (gHistory,gEnergy,gRestore)  {channel="mqtt:topic:mosquitto:dsmr:P_UseTotT2"}

// Item to store the actual tariff for the current consumption or return (Low=1, High=2) (received through MQTT message).
Number          Pwr_Tariff      "Tarief [MAP(tariff.map):%s]"   <piggybank> (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:P_Tariff"}

// Items to store meter reading of total gas consumption since install time of the DSMR.
// The gas meter updates it readings only once an hour in the DSMR 4.x standard (to save the battery, I guess).
Number:Volume   Gas_UseTot      "Meterstand Gas [%d m3]"        <gas>       (gHistory,gEnergy)          {channel="mqtt:topic:mosquitto:dsmr:G_UseTot"}

// Items to store meter readings of total return of energy since install time of the solar panels.
Number          Pwr_RetTotT1    "Retourmeter Dal [%d kWh]"      <energy>    (gHistory,gEnergy,gRestore) {channel="mqtt:topic:mosquitto:dsmr:P_RetTotT1"}
Number          Pwr_RetTotT2    "Retourmeter Hoog [%d kWh]"     <energy>    (gHistory,gEnergy,gRestore) {channel="mqtt:topic:mosquitto:dsmr:P_RetTotT2"}

The Pwr_Tariff item uses a mapping to translate the numbers received from the DSMR message (1 or 2) into human readable text. Create a mapping file $OPENHAB_CONF/transform/tariff.map to store the translations:

1=Low
2=Normal

Lights on: Philips Hue binding

We have a bunch of Philips Hue lights around the house, both inside and outside. They can all be controlled by openHAB. These Hue lights use the Zigbee Light Link protocol (ZLL) and all connect to a Hue Bridge device. Up to XX ddevices can connect to a bridge. If you have more devices, a second bridge mjust be added. Since we're still in the process of switching all lights to Hue, we're not yet over the limit, so one bridge is sufficient for our setup.

The standard Philips Hue binding is part of openHAB 2.4.0 and already installed before via the addons.cfg file. We can directly start configuring the bridge and lights. Using the Hue API we first create a user account on the Philips Hue Bridge. Open the Hue bridge debug page in your browser and POST the following command to /api/USERNAME/:

{"devicetype": "openhab2#haserver.myopenhab.org"}

This results in the creation of a whitelisted username on the Philips Hue bridge (after you pressed the button on the bridge), which we store in the Philips Hue binding configuration. The Hue API provides information like this (userid has been altered for security reasons):

"BwV8210ZezILkJpfelBisdZFvkP8HPgk4loHK1FM": {
    "last use date": "2018-12-22T06:19:47",
    "create date": "2018-12-22T05:50:32",
    "name": "openhab2#ha.openhab.org"
}

We have three types of Hue lights in and around our house: "Color temperature light", device type 0220, supporting the "brightness", "color_temperature", "alert" and "effect" channels, **"RGB"* lights, device type 0210, supporting the "on/off", "color", "color_temperature", "alert" and "effect" channels, and the "Dimmable light"**, device type 0100, supporting the "brightness" and "alert" channels.

The Things definition file for a Philips Hue bridge looks like the example from my configuration below. Note that I collected the unique id's from the log file when the binding is enabled in $OPENHAB_CONF/services/addons.cfg, and use this also in the channel definitions in the Items file below. Otherwise the log would be flooded with frequent discovery messages from the binding.

Earlier in 2018 Philips announced Homekit integration which makes interaction available from the Dimmers, Motion sensors and Taps. As of openHAB 2.4.0 the Hue Dimmer Switch and the Motion Sensor devices are also supported, while support for Hue Tap is in testing phase and targeted for the 2.5.0 release. Since we have several Tap devices around the house, I am currently using the new test version of this binding. More on the Dimmer, Tap and sensor devices in the next HA article of this blog, for now we focus on the Hue lights.

Bridge hue:bridge:0017863fb4b3 [ipAddress="192.168.1.98", userName="BwV8210WezILkJpfelDisdZFvkP8APgk4loHK2GM"]
{
    // Livingroom chair standing light
    0220 bulb_chair [lightId="1"]
    // Outside leftside house light
    0100 bulb_lhouse [lightId="2"]
    // Livingroom cornertable light
    0220 bulb_corner [lightId="3"]
    // Livingroom sidetable light
    0220 bulb_side [lightId="4"]
    // Outside left back light
    0100 bulb_lback [lightId="5"]
    // Livingroom backdoor standing light
    0220 bulb_bdoor [lightId="6"]
    // Livingroom couch light
    0220 bulb_couch [lightId="7"]
    // Outside right back light
    0100 bulb_rback [lightId="8"]
    // Bedroom left bed light
    0220 bulb_lbed [lightId="9"]
    // Bedroom right bed light
    0220 bulb_rbed [lightId="10"]
    // Outdoor toolshed left light
    0100 bulb_shed [lightId="11"]
    // Kitchen left light
    0220 bulb_lkitchen [lightId="13"]
    // Kitchen right light
    0220 bulb_rkitchen [lightId="14"]
    // Guestroom ceiling spotlights
    0220 bulb_guest1 [lightId="15"]
    0220 bulb_guest2 [lightId="16"]
    // Hallway wall light
    0220 bulb_hall [lightId="17"]
    // Outdoor terras lantern light
    0100 bulb_terrace [lightId="18"]
    // Laundry room ceiling light
    0220 bulb_laundry [lightId="19"]
    // Toilet ceiling light
    0220 bulb_toilet [lightId="20"]
    // Frontdoor light
    0100 bulb_frontdoor [lightId="21"]
    // Dining table lights
    0220 bulb_dining1 [lightId="22"]
    0210 bulb_dining2 [lightId="24"]
    0210 bulb_dining3 [lightId="23"]
}

More information on the Philips Hue openHAB binding can be found on the offical openHAB website. Our log file, again, gives proof of the proper authorization of the Philips Hue bridge:

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'hue.things'
[INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'hue.things'
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:1' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0100:0017863fb4b3:2' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:3' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:4' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0100:0017863fb4b3:5' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:6' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:7' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0100:0017863fb4b3:8' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:9' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:10' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0100:0017863fb4b3:11' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:13' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:14' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:15' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:16' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:17' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0100:0017863fb4b3:18' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:19' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:20' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0100:0017863fb4b3:21' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0220:0017863fb4b3:22' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0210:0017863fb4b3:23' to inbox.
[INFO ] [g.discovery.internal.PersistentInbox] - Added new thing 'hue:0210:0017863fb4b3:24' to inbox.

Next step is to define Items for all our Hue lights, so we can actually do something with them. This is how my hue.items configuration looks like (you'll see the relation between the channels and the definitions in the Things file):

//====================================================================================
// All the Hue lights are defined here, including some OH groups to handle them.
// Currently the Hue binding does not support Scenes and Groups defined in the bridge.
//====================================================================================

// Group for all lights
Group gLights "Verlichting"

// Livingroom chair light
Switch              SW_ChairLight       "Stoel Staandelamp"                                 (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_chair:brightness"}
Dimmer              DM_ChairLight       "Stoel Staandelamp dim"                             (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_chair:brightness"}
Dimmer              CT_ChairLight       "Stoel Staandelamp tint"                            (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_chair:color_temperature"}
// Livingroom cornertable light
Switch              SW_CornerLight      "Hoektafel Lamp"                                    (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_corner:brightness"}
Dimmer              DM_CornerLight      "Hoektafel Lamp dim"                                (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_corner:brightness"}
Dimmer              CT_CornerLight      "Hoektafel Lamp tint"                               (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_corner:color_temperature"}
// Livingroom sidetable light
Switch              SW_SideTblLight     "Sidetable Lamp"                                    (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_side:brightness"}
Dimmer              DM_SideTblLight     "Sidetable Lamp dim"                                (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_side:brightness"}
Dimmer              CT_SideTblLight     "Sidetable Lamp tint"                               (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_side:color_temperature"}
// Livingroom backdoor standing light
Switch              SW_BackdoorLight    "Achterdeur Lamp"                                   (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_bdoor:brightness"}
Dimmer              DM_BackdoorLight    "Achterdeur Lamp dim"                               (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_bdoor:brightness"}
Dimmer              CT_BackdoorLight    "Achterdeur Lamp tint"                              (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_bdoor:color_temperature"}
// Livingroom couch light
Switch              SW_CouchLight       "Bank Staandelamp"                                  (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_couch:brightness"}
Dimmer              DM_CouchLight       "Bank Staandelamp dim"                              (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_couch:brightness"}
Dimmer              CT_CouchLight       "Bank Staandelamp tint"                             (gLiving,gLights)           {channel="hue:0220:0017882ec5b3:bulb_couch:color_temperature"}

// Dining table lights
Switch              SW_DiningLight1     "Eettafel Lamp 1"                                   (gDining,gLights)           {channel="hue:0220:0017882ec5b3:bulb_dining1:brightness"}
Dimmer              DM_DiningLight1     "Eettafel Lamp 1 dim"                               (gDining,gLights)           {channel="hue:0220:0017882ec5b3:bulb_dining1:brightness"}
Dimmer              CT_DiningLight1     "Eettafel Lamp 1 tint"                              (gDining,gLights)           {channel="hue:0220:0017882ec5b3:bulb_dining1:color_temperature"}
Switch              SW_DiningLight2     "Eettafel Lamp 2"                                   (gDining,gLights)           {channel="hue:0210:0017882ec5b3:bulb_dining2:color"}
Dimmer              DM_DiningLight2     "Eettafel Lamp 2 dim"                               (gDining,gLights)           {channel="hue:0210:0017882ec5b3:bulb_dining2:color"}
Color               CO_DiningLight2     "Eettafel Lamp 2 kleur"                             (gDining,gLights)           {channel="hue:0210:0017882ec5b3:buld_dining2:color"}
Dimmer              CT_DiningLight2     "Eettafel Lamp 2 tint"                              (gDining,gLights)           {channel="hue:0210:0017882ec5b3:bulb_dining2:color_temperature"}
Switch              SW_DiningLight3     "Eettafel Lamp 3"                                   (gDining,gLights)           {channel="hue:0210:0017882ec5b3:bulb_dining3:color"}
Dimmer              DM_DiningLight3     "Eettafel Lamp 3 dim"                               (gDining,gLights)           {channel="hue:0210:0017882ec5b3:bulb_dining3:color"}
Color               CO_DiningLight3     "Eettafel Lamp 3 kleur"                             (gDining,gLights)           {channel="hue:0210:0017882ec5b3:buld_dining3:color"}
Dimmer              CT_DiningLight3     "Eettafel Lamp 3 tint"                              (gDining,gLights)           {channel="hue:0210:0017882ec5b3:bulb_dining3:color_temperature"}

// Hall wall light
Switch              SW_HallLight        "Hal Wandlamp"                                      (gHall,gLights)             {channel="hue:0220:0017882ec5b3:bulb_hall:brightness"}
Dimmer              DM_HallLight        "Hal Wandlamp dim"                                  (gHall,gLights)             {channel="hue:0220:0017882ec5b3:bulb_hall:brightness"}
Dimmer              CT_HallLight        "Hal Wandlamp tint"                                 (gHall,gLights)             {channel="hue:0220:0017882ec5b3:bulb_hall:color_temperature"}

// Kitchen lightS
Switch              SW_LKitchenLight    "Keukenlamp links"                                  (gKitchen,gLights)          {channel="hue:0220:0017882ec5b3:bulb_lkitchen:brightness"}
Dimmer              DM_LKitchenLight    "Keukenlamp links dim"                              (gKitchen,gLights)          {channel="hue:0220:0017882ec5b3:bulb_lkitchen:brightness"}
Dimmer              CT_LKitchenLight    "Keukenlamp links tint"                             (gKitchen,gLights)          {channel="hue:0220:0017882ec5b3:bulb_lkitchen:color_temperature"}
Switch              SW_RKitchenLight    "Keukenlamp rechts"                                 (gKitchen,gLights)          {channel="hue:0220:0017882ec5b3:bulb_rkitchen:brightness"}
Dimmer              DM_RKitchenLight    "Keukenlamp rechts dim"                             (gKitchen,gLights)          {channel="hue:0220:0017882ec5b3:bulb_rkitchen:brightness"}
Dimmer              CT_RKitchenLight    "Keukenlamp rechts tint"                            (gKitchen,gLights)          {channel="hue:0220:0017882ec5b3:bulb_rkitchen:color_temperature"}

// Toilet ceiling light
Switch              SW_ToiletLight      "Toilet Lamp"                                       (gToilet,gLights)           {channel="hue:0220:0017882ec5b3:bulb_toilet:brightness"}
Dimmer              DM_ToiletLight      "Toilet Lamp dim"                                   (gToilet,gLights)           {channel="hue:0220:0017882ec5b3:bulb_toilet:brightness"}
Dimmer              CT_ToiletLight      "Toilet Lamp tint"                                  (gToilet,gLights)           {channel="hue:0220:0017882ec5b3:bulb_toilet:color_temperature"}

// Bedroom lights
Switch              SW_LBedLight        "Bedlamp Ron"                                       (gBedroom,gLights)          {channel="hue:0220:0017882ec5b3:bulb_lbed:brightness"}
Dimmer              DM_LBedLight        "Bedlamp Ron dim"                                   (gBedroom,gLights)          {channel="hue:0220:0017882ec5b3:bulb_lbed:brightness"}
Dimmer              CT_LBedLight        "Bedlamp Ron tint"                                  (gBedroom,gLights)          {channel="hue:0220:0017882ec5b3:bulb_lbed:color_temperature"}
Switch              SW_RBedLight        "Bedlamp Arniël"                                    (gBedroom,gLights)          {channel="hue:0220:0017882ec5b3:bulb_rbed:brightness"}
Dimmer              DM_RBedLight        "Bedlamp Arniël dim"                                (gBedroom,gLights)          {channel="hue:0220:0017882ec5b3:bulb_rbed:brightness"}
Dimmer              CT_RBedLight        "Bedlamp Arniël tint"                               (gBedroom,gLights)          {channel="hue:0220:0017882ec5b3:bulb_rbed:color_temperature"}

// Guest ceiling spotlights
Switch              SW_GuestLight1       "Menno plafondspot 1"                              (gGuest,gLights)            {channel="hue:0220:0017882ec5b3:bulb_guest1:brightness"}
Dimmer              DM_GuestLight1       "Menno plafondspot 1 dim"                          (gGuest,gLights)            {channel="hue:0220:0017882ec5b3:bulb_guest1:brightness"}
Dimmer              CT_GuestLight1       "Menno plafondspot 1 tint"                         (gGuest,gLights)            {channel="hue:0220:0017882ec5b3:bulb_guest1:color_temperature"}
Switch              SW_GuestLight2       "Menno plafondspot 2"                              (gGuest,gLights)            {channel="hue:0220:0017882ec5b3:bulb_guest2:brightness"}
Dimmer              DM_GuestLight2       "Menno plafondspot 2 dim"                          (gGuest,gLights)            {channel="hue:0220:0017882ec5b3:bulb_guest2:brightness"}
Dimmer              CT_GuestLight2       "Menno plafondspot 2 tint"                         (gGuest,gLights)            {channel="hue:0220:0017882ec5b3:bulb_guest2:color_temperature"}

// Laundry room ceiling light
Switch              SW_LaundryLight      "Stephan plafondlamp"                              (gLaundry,gLights)          {channel="hue:0220:0017882ec5b3:bulb_laundry:brightness"}
Dimmer              DM_LaundryLight      "Stephan plafondlamp dim"                          (gLaundry,gLights)          {channel="hue:0220:0017882ec5b3:bulb_laundry:brightness"}
Dimmer              CT_LaundryLight      "Stephan plafondlamp tint"                         (gLaundry,gLights)          {channel="hue:0220:0017882ec5b3:bulb_laundry:color_temperature"}

// Outside lights
Switch              SW_LBHouseLight     "Lantaarn L.achter"                                 (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_lback:brightness"}
Dimmer              DM_LBHouseLight     "Lantaarn L.achter dim"                             (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_lback:brightness"}
Switch              SW_LHouseLight      "Lantaarn links"                                    (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_lhouse:brightness"}
Dimmer              DM_LHouseLight      "Lantaarn links dim"                                (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_lhouse:brightness"}
Switch              SW_RBHouseLight     "Lantaarn R.achter"                                 (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_rback:brightness"}
Dimmer              DM_RBHouseLight     "Lantaarn R.achter dim"                             (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_rback:brightness"}
Switch              SW_FrntDoorLight    "Voordeurlantaarn"                                  (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_frontdoor:brightness"}
Dimmer              DM_FrntDoorLight    "Voordeurlantaarn dimmer"                           (gOutside,gLights)          {channel="hue:0100:0017882ec5b3:bulb_frontdoor:brightness"}
// Garden lights
Switch              SW_TerraceLight     "Terraslantaarn"                                    (gGarden,gLights)           {channel="hue:0100:0017882ec5b3:bulb_terrace:brightness"}
Dimmer              DM_TerraceLight     "Terraslantaarn dimmer"                             (gGarden,gLights)           {channel="hue:0100:0017882ec5b3:bulb_terrace:brightness"}
Switch              SW_ToolsLight       "Tuinhuis Lantaarn"                                 (gGarden,gToolSh,gLights)   {channel="hue:0100:0017882ec5b3:bulb_shed:brightness"}
Dimmer              DM_ToolsLight       "Tuinhuis Lantaarn dim"                             (gGarden,gToolSh,gLights)   {channel="hue:0100:0017882ec5b3:bulb_shed:brightness"}

With these Items configured, we are ready to present them through the BasicUI.

Showing off: the Basic User Interface

OpenHAB comes with a variety of UI's to manage/configure stuff and to do your daily home automation things. The PaperUI interface is new in openHAB 2.x and primarily meant for configuration, but since we do everything with configuration files, I'll just ignore that interface for now. Two other interesting UI's are Basic UI and HABpanel. Since HABpanel needs a little more work to get good results (but can create a much nicer UI in the end), we'll setup our HA system with Basic UI and once everything is working as expected, we will dive into HABpanel.

So far, everything happend in the dark corners of our HA server. Time to show some skin and create our first UI part starting with the Astro, Hue and DSMR data. Basic UI works with a .sitemap file that is stored in $OPENHAB_CONF/sitemaps/<somefile.sitemap>. There can be more than one sitemap, and you can switch between them. The default sitemap file is specified in the openHAB configuration (you can see/change in in the PaperUI interface or in $OPENHAB_XXXX/xxxx/xxxxxx/x/x/x/x). Let's start with a simple sitemap for some basic Astro, Hue and DSMR Items. Create a new file $OPENHAB_CONF/sitemaps/home.sitemap and paste the following in the empty file. Note that some names and panels have description that will make sense once we've expanded our configuration, but may like strange right now.

sitemap home label="At Home" {
    Frame label="Climate" {
        Text item=Current_DateTime  label="Date [%1$tA, %1$td.%1$tm.%1$tY]"
        Text item=Sunrise_Time                                               visibility=[Sun_Elevation <= 0]
        Text item=Sunset_Time                                                visibility=[Sun_Elevation > 0]
        Text item=Sun_Elevation                                              visibility=[Sun_Elevation > 0]
        Text item=Sun_Direction                                              visibility=[Sun_Elevation >= 0]
    }
    Frame label="Energy" {
        Text item=Pwr_Use visibility=[Pwr_Use > 0] {
            Text item=Pwr_UseL1
            Text item=Pwr_UseL2
            Text item=Pwr_UseL3
        }
        Text item=Pwr_Ret visibility=[Pwr_Ret > 0] {
            Text item=Pwr_RetL1
            Text item=Pwr_RetL2
            Text item=Pwr_RetL3
        }
        Text item=Pwr_Tariff
        Text item=Pwr_UseTotT1
        Text item=Pwr_UseTotT2
        Text item=Pwr_RetTotT1
        Text item=Pwr_RetTotT2

        Text item=Gas_UseTot
    }
    Frame label="Lights" {
        Switch item=SW_ChairLight
        Switch item=SW_CornerLight
        Switch item=SW_SideTblLight
        Switch item=SW_BackdoorLight
        Switch item=SW_CouchLight
        Switch item=SW_HallLight
        Switch item=SW_LKitchenLight
        Switch item=SW_RKitchenLight
        Switch item=SW_DiningLight1
        Switch item=SW_DiningLight2
        Switch item=SW_DiningLight3
        Switch item=SW_ToiletLight
        Switch item=SW_LBedLight
        Switch item=SW_RBedLight
        Switch item=SW_LaundryLight
        Switch item=SW_LBHouseLight
        Switch item=SW_LHouseLight
        Switch item=SW_RBHouseLight
        Switch item=SW_FrntDoorLight
        Switch item=SW_TerraceLight
        Switch item=SW_ToolsLight
        Switch item=SW_GuestLight1
        Switch item=SW_GuestLight2
    }
}

The name of the file should match the name in the sitemap (home in this example). We give it also a label that is displayed on the screen ("At Home"). Inside the sitemap we can define frames that group different items together, again with a label that is displayed on the frame surrounding the items in the browser or App. Notice that items are defined as Text and not as Number. Sitemaps only know the concept of Text and Switch elements, and Numbers are converted to text before display. Some elements have a conditional visibility parameter: if the condition is true the item is displayed otherwise it is hidden. For instance, the time when the sun sets is only displayed while the sun is above the horizon.

Simple openHAB sitemap example

Notice the small border around the Frames defined in the sitemap. On text items, using { }, creates a 'subgroup' that opens wheb clicking on the Item. There we can present detailed data related the main item. For instance, when clicking on the PowerUse item a new frame is shown with the current power consumption for each of the 3 phases of electricty in our house. We can change the look of sitemaps a little by specifying other icons (we can store custom icons in `$OPENHAB_COMF/icons). Details can be found in the openHAB Items documentation. We'll look into that in a next article, for now we use what's available in openHAB. There are more options to change the look-and-feel, including using CSS and custom HTML. We'll work on that when creating graphs.

Further configuration and additional bindings will be addressed in the next blog post.

The configuration files shown here are just example snapshots of the actual configuration. For the complete and actual up-to-date configuration, please check my Github repository where it will be available soon.

Add a comment

Previous Post

Add a comment