A Dreamer's Lair

Migrating away from Elv MAX! heating

After having used the Elv MAX! heating solution for several years now, I have decided to finally move away from it. It simply is not stable enough for a longer period. Every couple of months the MAX! Cube loses its configuration or part of it. And then I have to pair every thermostat and radiator valve again.

However until recently I couldn’t come up with an affordable alternative to it. Stuff¬† like the Danfoss heating zones is very expensive. There are ZWave valves available but the limitiation with them is that they can only be controlled with a set temperature. And (of course) you don’t want to control a room from the temperature reading from several radiator thermostats but from a central reading on a room thermostat of some sort. But the most important issue with this is, that I have ‘city heating’. The hot water enters my house and flows into the radiators. This hot water flow is regulated with a ZWave switch which turns the flow on or off. For this to work I need to know when to turn on the boiler switch. If one would do this using the reported temperatures I think its level of control is not fine grained enough.

Fortunately there seems to be a new player on the market: the¬†EUROtronic Spirit Z-Wave Plus. It’s an affordable ZWAVE radiator thermostat which has the extra feature that you can choose to report and control the valve opening. With this option I can now see how far the radiator valves have been opened. And when one of them reaches the threshold (at for example 25% opening) I can actuate the boiler switch to let the city heating water flow into the house.

Since I am migrating to a ZWave Z-Stick solution anyway I might also start an experiment with one (or a few) of these newer radiator thermostats to see how this performs.

This is also important since I am having an airco/Heat pump installed into the living room. The plan is to let both the city heating and the heat pump work together to get a comfortable temperature in the living room.

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.

eQ-3 Max Cube message protocol decrypted: Part 3, the ‘M’ word

In this part of the series of articles about the Cube Message Protocol, I’m going to discuss the Metadata message, the ‘M’ message. It 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 m: command.

The metadata contains, as the name already tells us, metadata about our collection of eQ-3 devices.

Like the H message, the M message contains a comma separated list of strings in the form of

          
M:00,01,VgIGAgpMaXZpbmdSb29tDMAeAwRIYWxsC64PBQhCYXRoUm9v=
          

The first two parameters are (yet) unknown. The third and last string is a Base64 encoded string. It contains the definitions for all rooms (or groups if you will) and devices. The decoded bytes will look something like this

          
0000: 56 02 06 02 0a 4c 69 76   V....Liv
0008: 69 6e 67 52 6f 6f 6d 0c   ingRoom.
0010: c0 1e 03 04 48 61 6c 6c   ....Hall
0018: 0b ae 0f 05 08 42 61 74   .....Bat
0020: 68 52 6f 6f 6d 06 c9 22   hRoom...
0028: 04 07 42 65 64 52 6f 6f   ..BedRoo
0030: 6d 0b aa e9 01 09 48 6f   m.....Ho
0038: 62 62 79 52 6f 6f 6d 06   bbyRoom.
0040: c9 41 06 09 53 70 61 72   .A..Spar
0048: 65 52 6f 6f 6d 0c c0 51   eRoom..Q
0050: 0f 03 0a 12 bd 4b 45 51   .....KEQ
0058: 30 37 30 34 37 35 32 13   0704752.
0060: 54 68 65 72 6d 6f 73 74   Thermost
0068: 61 74 48 6f 62 62 79 52   atHobbyR
0070: 6f 6f 6d 01 01 06 c9 41   oom....A
0078: 4b 45 51 30 33 35 32 32   KEQ03522
0080: 37 36 15 52 61 64 69 61   76.Radia
0088: 74 6f 72 48 6f 62 62 79   torHobby
0090: 52 6f 6f 6d 52 65 61 72   RoomRear
0098: 01 01 06 c8 0c 4b 45 51   .....KEQ
          

The metadata contains the following information

          
Position   Information
===================================================
00:        Meta Data Magic. Should always be 0x56
01:        Meta data version. Should always be 0x02
02:        Number of Rooms
xx-(yy-1): Meta Data for each room.
yy:        Number of Devices
(yy+1)-zz: Meta Data for each device
          

The magic number and meta data version always seem a fixed number. Perhaps the meta data version will be increased when a new meta data version is composed by eQ-3. So perhaps it’s best to check in your code that both numbers are 0x56 and 0x02 and log a warning if they ever change.

After the first two bytes, the next byte indicates the number of rooms (or groups) as defined in the Cube followed by a sequence of blocks of data, one for each room. The metadata for each room looks something like this

          
0000:          02 0a 4c 69 76   V....Liv
0008: 69 6e 67 52 6f 6f 6d 0c   ingRoom.
0010: c0 1e                     ..

Position    Information
=========================================
00:         Room ID (1 byte)
01:         Length of Room Name (1 byte)
02-(xx-1):  Room Name
xx:         Room Master Address (3 bytes)
          

In the above example, the Room has an ID of 2, contains a name of 10 (0x0a) characters which is LivingRoom and has an address of 0x0cc01e which translates as decimal 835614.

After the sequence of rooms, the next byte contains the number of devices followed by a sequence of blocks of data, one for each device. The metadata for each device looks something like this

          
0050:    03 0a 12 bd 4b 45 51   .....KEQ
0058: 30 37 30 34 37 35 32 13   0704752.
0060: 54 68 65 72 6d 6f 73 74   Thermost
0068: 61 74 48 6f 62 62 79 52   atHobbyR
0070: 6f 6f 6d 01               oom.

Position    Information
==========================================
00:         Device Type (1 byte)
01:         RF Address (3 byte)
04:         Serial Number (10 bytes)
0E:         Length of Device Name (1 byte)
0F-(xx-1):  Device Name
xx:         Room ID
          

In the above example the device type is 3, the address is 0x0a12bd which translates as decimal 660157. The serial number for this device is KEQ0704752. The name is 19 (0x13) bytes long with the value of ThermostatHobbyRoom. Lastly, the device is part of room with ID 1.

I have defined the device type, in C#, as an enumeration with the following definition

          
/// <summary>
/// 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.
/// </summary>
public enum MaxDeviceType
{
    Cube = 0,
    RadiatorThermostat = 1,
    RadiatorThermostatPlus = 2,
    WallThermostat = 3,
    ShutterContact = 4,
    EcoButton = 5
}
          

So in the above example the device (type 3) would be a Wall Thermostat Device.

And that’s it for the M message!