Add basic user/password authentication to an openHAB 2 docker container running on a Synology NAS (with DSM 6.1)
While the old openHAB 1.x version has some form of simple authentication protection (user/password), to try to keep out unwanted visitors turning on your lights and whatnot, unfortunately openHAB version 2 does not (yet?).
On the openHAB site there are some examples on how to add this using for example a reverse proxy. Perhaps the information on openHAB makes good sense to the linux oriented out there, but since I am a windows man myself, for me it did not.
I tried to install stuff like Traefik and nginx in a container on my Synology NAS and play with it. But however I tried, they did not even work at all to begin with, let alone in combination with my openHAB 2 docker container.
Luckily my Synology NAS is running DSM 6.1 which has built-in nginx for DSM to use. But it is also perfectly suited to be used as a reverse proxy for any docker container you are running, including openHAB 🙂
You can add extra configuration to nginx which DSM is using to configure whatever you want.
In this case I wanted to add some user/password authentication to the openHAB container to prevent unwanted access. So when for example I browse to http://openhab.adreamerslair.nl, it should ask for a user/password and forward the call to the openHAB container.
Of course the first part for this to work is routing the subdomain openhab.adreamerslair.nl to my home IP-address. Then my home router should forward any calls to port 80 to the Synology NAS. For the sake of this post, I assume this has already been taken care of.
On the Synology NAS running DSM6.1, you can add custom nginx configuration files to the following folder
/usr/local/etc/nginx/sites/sites-enabled
I created a file called openhab.conf in this folder with the following contents (as borrowed from the openHAB site)
server { listen 80; server_name openhab.adreamerslair.nl; location / { auth_basic "Login"; auth_basic_user_file /volume1/dockerdata/.htpasswd; proxy_pass http://localhost:8080; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
It essentially does the following
which is exactly what we want! If the request is not sent to port 80 and the specific domain then the request is ignored and handled elsewhere in nginx.
Which users/password are used in the authentication are defined with
auth_basic_user_file /volume1/dockerdata/.htpasswd
Make sure you have a .htpasswd file somewhere on your NAS and define the path to it here. The .htpasswd file can be created using the htpasswd tool in linux. But if you are, like me, a windows guy (or girl) you can use for example this online tool Use it to generate the authentication string and paste it in the .htpasswd file (one user per line!).
Now you just have to restart nginx on the NAS
And there we have it. Basic user/password authentication on our openHAB2 🙂
How to add the correct Timezone to OpenHAB2 running in a docker container on a Synology NAS
Recently I decided to take the plunge in upgrading my running OpenHAB version 1.x system to the latest version 2.1. I also wanted to take this opportunity to let OpenHAB run in a Docker container since the Synology NAS I bought a while back supports Docker.
I won’t go into the details of upgrading OpenHAB release 1.x to 2.1 in this post, since the OpenHAB site itself has enough documentation on that subject.
However I found out that there are some difficulties in using OpenHAB2 running in a Docker container on a Synology NAS. On the Docker page of OpenHAB it seems easy enough to create an OpenHAB container using the given statement:
docker run \ --name openhab \ --net=host \ -v /etc/localtime:/etc/localtime:ro \ -v /etc/timezone:/etc/timezone:ro \ -v /opt/openhab/conf:/openhab/conf \ -v /opt/openhab/userdata:/openhab/userdata \ -v /opt/openhab/addons:/openhab/addons\ -d \ --user=<uid> \ --restart=always \ openhab/openhab:<version>-<architecture>
The first difficulty, with my limited knowledge of Synology/Linux, was creating a user for OpenHAB and passing it on to the container (with –user=
I did not solve that problem, but since I also don’t have the need for any UPNP autodiscovery in OpenHAB2 (since all communication in my domotica system goes through the use of MQTT messages), I could omit –net=host.
Now I have a container which is secure enough for me.
The next problem, and the subject of this post, was that my OpenHAB2 container was running in the Zulu ‘Timezone’ so time was two hours ‘off’. This is not very pleasant when you have rules which are time based (e.g. sunset etc). I searched a hole in the ground for a solution and tried all sorts of things, but I finally found the answer on a website (unfortunately forgot which one) and wanted it to share for those who are also trying to use OpenHAB2 in a Docker container on a Synology NAS.
The Timezone should be correct because you use -v /etc/localtime:/etc/localtime:ro and -v /etc/timezone:/etc/timezone:ro but it wasn’t. There is also the possibility of setting the timezone in OpenHAB itself using an NTP thing. But that also didn’t work.
Finally I found out that one has to use an extra JAVA_OPT environment variable passed to the container. And that did work!
So what I eventually did to make things work was:
-Create a folder on my NAS to hold the OpenHAB data let’s say with the name ‘folder’
-Create the necessary subfolders, in this case addons, conf and userdata
And then lastly I could create and start the container through the following command line (in Synology SSH shell)
sudo docker run \ --name openhab \ --tty \ -p 8080:8080 \ -v /etc/localtime:/etc/localtime:ro \ -v /etc/TZ:/etc/timezone:ro \ -v /volume1/folder/addons:/openhab/addons \ -v /volume1/folder/conf:/openhab/conf \ -v /volume1/folder/userdata:/openhab/userdata \ -e EXTRA_JAVA_OPTS="-Duser.timezone=Europe/Amsterdam" \ -d \ --restart=always \ openhab/openhab:2.1.0-amd64
The trick here is to use
-e EXTRA_JAVA_OPTS="-Duser.timezone=Europe/Amsterdam" \
to pass the wanted Timezone to OpenHAB. Now the container runs smoothly and with the correct time 🙂
eQ-3 Max Cube message protocol decrypted: Part 4, the ‘C’ word
In this part of unravelling the secrets of the eQ-3 Max Cube, I shall tell something about the Configuration Message Response, or the C Message Response. Like the H and M messages, the C message is one of the messages that the cubes sends when connecting to it for the first time. Also the message can be requested by using the c: command.
The configuration message contains, as the name would suggest, configuration data for a specific device which has been paired to the Cube. This means that, when a connection has been made to the Cube, a C message response will be received for each device that has been paired to the Cube.
Like the M message, the C message contains a comma separated list of strings in the form of
C:03f25d,7QPyXQATAQBKRVEwNTQ0OTIzAAsABEAAAAAAAAAAAPIA==
The first field contains the address of the device in HEX form. Because the eQ-3 software displays addresses in decimal notation, the address in this example would be 0x03f25d = 258653.
The second and last field contains a Base64 encoded string with the configuration data for the device (as identified by its address). The decoded bytes will look something like this for the Cube device itself
0000: ed 03 f2 5d 00 13 01 00 ........ 0008: 4a 45 51 30 35 34 34 39 JEQ05449 0010: 32 33 00 0b 00 04 40 00 23...... 0018: 00 00 00 00 00 00 00 ff ........ 0020: ff ff ff ff ff ff ff ff ........ 0028: ff ff ff ff ff ff ff ff ........ 0030: ff ff ff ff 0b 00 04 40 ........ 0038: 00 00 00 00 00 00 00 41 .......A 0040: ff ff ff ff ff ff ff ff ........ 0048: ff ff ff ff ff ff ff ff ........ 0050: ff ff ff ff ff 68 74 74 .....htt 0058: 70 3a 2f 2f 6d 61 78 2e p://max. 0060: 65 71 2d 33 2e 64 65 3a eq-3.de: 0068: 38 30 2f 63 75 62 65 00 80/cube. 0070: 30 2f 6c 6f 6f 6b 75 70 0/lookup 0078: 00 00 00 00 00 00 00 00 ........ 0080: 00 00 00 00 00 00 00 00 ........ 0088: 00 00 00 00 00 00 00 00 ........ 0090: 00 00 00 00 00 00 00 00 ........ 0098: 00 00 00 00 00 00 00 00 ........ 00a0: 00 00 00 00 00 00 00 00 ........ 00a8: 00 00 00 00 00 00 00 00 ........ 00b0: 00 00 00 00 00 00 00 00 ........ 00b8: 00 00 00 00 00 00 00 00 ........ 00c0: 00 00 00 00 00 00 00 00 ........ 00c8: 00 00 00 00 00 00 00 00 ........ 00d0: 00 00 00 00 00 00 43 45 ......CE 00d8: 54 00 00 0a 00 03 00 00 T....... 00e0: 0e 10 43 45 53 54 00 03 ..CEST.. 00e8: 00 02 00 00 1c 20 ......
The first 18 bytes seem to contain the same type of information for all devices (except the Cube which seems to have one difference). The rest of the information is device specific.
So the first 18 bytes contain the following information
Position Length Information =================================================== 00 1 Data Length 01 3 Address of device 04 1 Device Type 05 1 Room ID 06 1 Firmware version 07 1 Test Result 08 10 Serial Number
So in the example above, the data length would be 237 bytes (0xed), the address is 0x03f25d (which is the same as the first field of the C message), the device type is 0 (meaning the Cube device itself). The Room ID and firmware version seem to be combined to the firmware version only when it concerns the cube device where Room ID is the LSB and Firmware version the MSB both of which are BCD encoded. So in this specific case the firmware version of the cube is 0113 which equals to 1.1.3. I haven’t got a clue where the field test result is for. It always seems 0. Lastly in the common block of device data, the serial number of the device is specified. In this case JEQ0544923.
The device type, as we have already seen in the M message is encoded as
/// /// Enumeration of possible Max Device Types. /// The int value of the enumeration is important since it links /// to the device type number in the MAX device. /// public enum MaxDeviceType { Cube = 0, RadiatorThermostat = 1, RadiatorThermostatPlus = 2, WallThermostat = 3, ShutterContact = 4, EcoButton = 5 }
The device specific bytes are specific (hence device specific ;)) for a certain device type. The first byte in the generic part gives us the total of bytes in the C message so we know how long the message should be and what to expect. When it comes to the C message for the Cube, I don’t have much data available yet. So there are a lot of unknowns here as you can see
Position Length Information =================================================== 0012 1 Is Portal Enabled 0013-0054 66 Unknown 0055-???? ?? Portal URL ????-00ed ?? Unknown
The only thing I know now is the flag which indicates if the portal function has been enabled or not. And the Portal UR itself. In this case the URL seems to be http://max.eq-3.de:80/cube. In contrast to other fields the URL does not seem to have a field length specified as the first byte. Instead, the URL string seems to be terminated with a null byte. At the end of the message we can see some time zones specified but its unsure for me what the exact meaning is. So for now, this is the Cube C message.
For a radiator thermostat the decoded message looks something like this
0000: d2 06 c9 41 01 01 18 ff ...A.... 0008: 4b 45 51 30 33 35 32 32 KEQ03522 0010: 37 36 24 20 3d 09 07 18 76...... 0018: 03 f4 0c ff 00 41 20 41 .....A.A 0020: 20 41 20 41 20 41 20 41 .A.A.A.A 0028: 20 41 20 45 20 45 20 45 .A.E.E.E 0030: 20 45 20 45 20 45 20 41 .E.E.E.A 0038: 20 41 20 41 20 41 20 41 .A.A.A.A 0040: 20 41 20 41 20 45 20 45 .A.A.E.E 0048: 20 45 20 45 20 45 20 45 .E.E.E.E 0050: 20 41 20 41 20 41 20 41 .A.A.A.A 0058: 20 41 20 41 20 41 20 45 .A.A.A.E 0060: 20 45 20 45 20 45 20 45 .E.E.E.E 0068: 20 45 20 41 20 41 20 41 .E.A.A.A 0070: 20 41 20 41 20 41 20 41 .A.A.A.A 0078: 20 45 20 45 20 45 20 45 .E.E.E.E 0080: 20 45 20 45 20 41 20 41 .E.E.A.A 0088: 20 41 20 41 20 41 20 41 .A.A.A.A 0090: 20 41 20 45 20 45 20 45 .A.E.E.E 0098: 20 45 20 45 20 45 20 41 .E.E.E.A 00a0: 20 41 20 41 20 41 20 41 .A.A.A.A 00a8: 20 41 20 41 20 45 20 45 .A.A.E.E 00b0: 20 45 20 45 20 45 20 45 .E.E.E.E 00b8: 20 41 20 41 20 41 20 41 .A.A.A.A 00c0: 20 41 20 41 20 41 20 45 .A.A.A.E 00c8: 20 45 20 45 20 45 20 45 .E.E.E.E 00d0: 20 45 20 .E.
We can see that the data block is 210 (0xd2) bytes long. The address is 0x06c941 or decimal 444737. The device type is 1 (which means it’s a radiator thermostat) and it belongs to room 1. The firmware version is 24 (0x18) and the test result, whatever it means, is 255 (0xff). Finally the serial number is KEQ0352276.
The device specific bytes contain the following information
Pos Len Information ================================================================ 12 1 Comfort Temperature in degrees celsius * 2 13 1 Eco Temperature in degrees celsius * 2 14 1 Max Set Point Temperature in degrees celsius * 2 15 1 Min Set Point Temperature in degrees celsius * 2 16 1 Temperature offset in degrees celsius * 2 + 3,5 17 1 Window Open Temperature in degrees celsius * 2 18 1 Window Open Duration in minutes * 5 19 1 Boost 3 MSB bits are duration: value is in minutes * 5 but value 7 means 60 min. 5 LSB bits are valve opening in % * 5 1a 1 Decalcification 3 MSB bits are day of week: Saturday = 0 etc. 5 LSB bits are time in hours 1b 1 Max Valve Setting in % * 255 / 100 (so to get valve setting use value * 100 / 255) 1c 1 Valve Offset in % * 255 / 100 1d 182 Weekly Program Schedule of 26 bytes for each day starting with Saturday. Each schedule consists of 13 words (2 bytes) e.g. set points. 1 set point consist of 7 MSB bits is temperature set point (in degrees * 2) 9 LSB bits is until time (in minutes * 5)
For the above example data this translates to
Raw Decoded ================================================================ Comfort Temp 24 18 degrees Celsius Eco Temp 20 16 degrees Celsius Max Temp 3d 30,5 degrees Celsius Min Temp 09 4,5 degrees Celsius Temp Offset 07 0 degrees Celsius Win Open Temp 18 12 degrees Celsius Win Open Dur 03 15 minutes Boost f4 111 10100 -> 60 minutes, 100 % Decal 0c 000 01100 -> Saturday, 12:00 hours Max Valve ff 100 % Valve offset 00 0 % Weekly program 41 20 0100000 100100000 -> 16 degrees, until 24:00 etc.
For a wall thermostat the decoded message looks something like this
0000: ce 0a 12 bd 03 01 10 ff ........ 0008: 4b 45 51 30 37 30 34 37 KEQ07047 0010: 35 32 24 20 3d 09 41 20 52....A. 0018: 41 20 41 20 41 20 41 20 A.A.A.A. 0020: 41 20 41 20 45 20 45 20 A.A.E.E. 0028: 45 20 45 20 45 20 45 20 E.E.E.E. 0030: 41 20 41 20 41 20 41 20 A.A.A.A. 0038: 41 20 41 20 41 20 45 20 A.A.A.E. 0040: 45 20 45 20 45 20 45 20 E.E.E.E. 0048: 45 20 41 20 41 20 41 20 E.A.A.A. 0050: 41 20 41 20 41 20 41 20 A.A.A.A. 0058: 45 20 45 20 45 20 45 20 E.E.E.E. 0060: 45 20 45 20 41 20 41 20 E.E.A.A. 0068: 41 20 41 20 41 20 41 20 A.A.A.A. 0070: 41 20 45 20 45 20 45 20 A.E.E.E. 0078: 45 20 45 20 45 20 41 20 E.E.E.A. 0080: 41 20 41 20 41 20 41 20 A.A.A.A. 0088: 41 20 41 20 45 20 45 20 A.A.E.E. 0090: 45 20 45 20 45 20 45 20 E.E.E.E. 0098: 41 20 41 20 41 20 41 20 A.A.A.A. 00a0: 41 20 41 20 41 20 45 20 A.A.A.E. 00a8: 45 20 45 20 45 20 45 20 E.E.E.E. 00b0: 45 20 41 20 41 20 41 20 E.A.A.A. 00b8: 41 20 41 20 41 20 41 20 A.A.A.A. 00c0: 45 20 45 20 45 20 45 20 E.E.E.E. 00c8: 45 20 45 20 06 18 f4 E.E....
We can see that the data block is 206 (0xce) bytes long. The address is 0x0a12bd or decimal 660157. The device type is 3 (which means it’s a wall thermostat) and it belongs to room 1. The firmware version is 16 (0x10) and the test result, whatever it means, is 255 (0xff). Finally the serial number is KEQ0704752.
The device specific bytes contain the following information
Pos Len Information ================================================================ 12 1 Comfort Temperature in degrees celsius * 2 13 1 Eco Temperature in degrees celsius * 2 14 1 Max Set Point Temperature in degrees celsius * 2 15 1 Min Set Point Temperature in degrees celsius * 2 1d 182 Weekly Program Schedule of 26 bytes for each day starting with Saturday. Each schedule consists of 13 words (2 bytes) e.g. set points. 1 set point consist of 7 MSB bits is temperature set point (in degrees * 2) 9 LSB bits is until time (in minutes * 5) cc 3 Unknown
For the above example data this translates to
Raw Decoded ================================================================ Comfort Temp 24 18 degrees Celsius Eco Temp 20 16 degrees Celsius Max Temp 3d 30,5 degrees Celsius Min Temp 09 4,5 degrees Celsius Weekly program 41 20 0100000 100100000 -> 16 degrees, until 24:00 etc.
Since the shutter contact and eco switch doesn’t seem to have configuration data (and I don’t yet own them so can’t verify it either), this concludes this part of the series.