Feature Request: ELV MAX! support

Added by sj3fk3 about 2 years ago

It would be nice to control these babies from within Domotiga in the future. Decoding of the protocol is still ongoing here


Replies (28)

RE: Feature Request: ELV MAX! support - Added by rdnzl about 2 years ago

I have a MAX! cube and thermostat hidden somewhere in my pile of hardware, will try to find it for starters...

RE: Feature Request: ELV MAX! support - Added by sj3fk3 almost 2 years ago

Well if there's anything I can help with let me know.. I've set it up with a handfull of radiator thermostats and one window sensor.

RE: Feature Request: ELV MAX! support - Added by sj3fk3 over 1 year ago

There is some support for the ELV MAX! devices in the codebase now, but for me I'm not getting any sensible info from my radiotor thermostats. They have values ranging from 50 to 54 I have no clue what these values mean. I think "wwolkers" did the code so far? Is he still working on it? Do you guys know anything about further development?

RE: Feature Request: ELV MAX! support - Added by wwolkers over 1 year ago

Still working, as in waiting at the moment for some database changes.
I either have to use a separate table for the ELV devices, or create multiple devices in domotiga per physical device, since almost all of them contain more than 4 values.

Please show me some debug info from the 50-54 range values you see, since for me it works so far. Also, what version of the Max! firmware do you have in your cube?

RE: Feature Request: ELV MAX! support - Added by sj3fk3 over 1 year ago

2013/01/07 22:51:33 [ELVMAX] Received: H:IEQ0113715,00b60b,0111,00000000,16a940c7,00,32,0d0107,1633,03,0000
2013/01/07 22:51:33 [ELVMAX] SerialNumber: 'IEQ0113715'

Firmware 1.3.6

I'm going to mail the log, the server times-out when I try to post the debug file..

RE: Feature Request: ELV MAX! support - Added by sj3fk3 5 months ago

I would still love full support for the ELV MAX system.. Together with the Openthem gateway it's a killer feature!

RE: Feature Request: ELV MAX! support - Added by Alexie 5 months ago

What is exactly missing in the current ELV MAX implementation? ;) I don't have an ELV MAX, but just curious whats missing, maybe we can add it (the information is available ofcourse)

RE: Feature Request: ELV MAX! support - Added by sj3fk3 5 months ago

See my msg from 10 months ago.. Only getting 1 value per radiator and the value makes no sense. There should be more data per radiator. See for a full description of the protocol: http://www.domoticaforum.eu/viewtopic.php?f=66&t=6654 and http://www.fhemwiki.de/wiki/MAX

RE: Feature Request: ELV MAX! support - Added by Alexie 5 months ago

I will have a look into the mentioned topic, but do you have some debugging logging of the current information received from the ELV MAX? (i don't have such ELV MAX, and isn't on my list to buy either)

RE: Feature Request: ELV MAX! support - Added by sj3fk3 5 months ago

2013/11/11 20:57:39 [ELVMAX] Received: H:IEQ0113715,00b60b,0111,00000000,2be0dbb3,00,32,0d0b0b,1439,03,0000
2013/11/11 20:57:39 [ELVMAX] SerialNumber: 'IEQ0113715'
2013/11/11 20:57:39 [ELVMAX] Received: M:00,01,VgIFAQhiYWRrYW1lcgKSTQIFQW5vdWsCiUEDBlpvbGRlcgKSbAQKQmFieSBrYW1lcgM6PAUHS2FudG9vcgM3uAUBApJNSUVRMDUwODM0NwtSVCBiYWRrYW1lcgEBAolBSUVRMDUwMTE3NghSVCBBbm91awIBApJsSUVRMDUwODM3MglSVCBab2xkZXIDAQM6PElFTjAwNDYzNTYMUlQgQmFieWthbWVyBAEDN7hJRU4wMDQ1NzQ0ClJUIEthbnRvb3IFAQ==
2013/11/11 20:57:39 [ELVMAX] M: 56020501086261646B616D657202924D0205416E6F756B02894103065A6F6C64657202926C040A42616279206B616D6572033A3C05074B616E746F6F720337B8050102924D494551303530383334370B5254206261646B616D657201010289414945513035303131373608525420416E6F756B020102926C49455130353038333732095254205A6F6C6465720301033A3C49454E303034363335360C525420426162796B616D657204010337B849454E303034353734340A5254204B616E746F6F7205
2013/11/11 20:57:39 [ELVMAX] 56020501086261646B616D657202924D0205416E6F756B02894103065A6F6C64657202926C040A42616279206B616D6572033A3C05074B616E746F6F720337B8050102924D494551303530383334370B5254206261646B616D657201010289414945513035303131373608525420416E6F756B020102926C49455130353038333732095254205A6F6C6465720301033A3C49454E303034363335360C525420426162796B616D657204010337B849454E303034353734340A5254204B616E746F6F7205
2013/11/11 20:57:39 [ELVMAX] Found '5' rooms.
2013/11/11 20:57:39 [ELVMAX] M: #rooms: '5' RoomNumber: '1' RoomName: 'badkamer' 1stAddressInRoom: '02924D'
2013/11/11 20:57:39 [ELVMAX] M: #rooms: '5' RoomNumber: '2' RoomName: 'Anouk' 1stAddressInRoom: '028941'
2013/11/11 20:57:39 [ELVMAX] M: #rooms: '5' RoomNumber: '3' RoomName: 'Zolder' 1stAddressInRoom: '02926C'
2013/11/11 20:57:39 [ELVMAX] M: #rooms: '5' RoomNumber: '4' RoomName: 'Baby kamer' 1stAddressInRoom: '033A3C'
2013/11/11 20:57:39 [ELVMAX] M: #rooms: '5' RoomNumber: '5' RoomName: 'Kantoor' 1stAddressInRoom: '0337B8'
2013/11/11 20:57:39 [ELVMAX] Found 5 devices.
2013/11/11 20:57:39 [ELVMAX] M: DeviceType: '1' DeviceAddress: '02924D' DeviceName: 'RT badkamer'
2013/11/11 20:57:39 [ELVMAX] M: DeviceType: '1' DeviceAddress: '028941' DeviceName: 'RT Anouk'
2013/11/11 20:57:39 [ELVMAX] M: DeviceType: '1' DeviceAddress: '02926C' DeviceName: 'RT Zolder'
2013/11/11 20:57:39 [ELVMAX] M: DeviceType: '1' DeviceAddress: '033A3C' DeviceName: 'RT Babykamer'
2013/11/11 20:57:39 [ELVMAX] M: DeviceType: '1' DeviceAddress: '0337B8' DeviceName: 'RT Kantoor'
2013/11/11 20:57:39 [ELVMAX] Received: C:00b60b,7QC2CwARAf9JRVEwMTEzNzE1AQsABEAAAAAAAAAAAP///////////////////////////wsABEAAAAAAAAAAQf///////////////////////////2h0dHA6Ly93d3cubWF4LXBvcnRhbC5lbHYuZGU6ODAvY3ViZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENFVAAACgADAAAOEENFU1QAAwACAAAcIA==
2013/11/11 20:57:39 [ELVMAX] C: C:00b60b,7QC2CwARAf9JRVEwMTEzNzE1AQsABEAAAAAAAAAAAP///////////////////////////wsABEAAAAAAAAAAQf///////////////////////////2h0dHA6Ly93d3cubWF4LXBvcnRhbC5lbHYuZGU6ODAvY3ViZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAENFVAAACgADAAAOEENFU1QAAwACAAAcIA==
2013/11/11 20:57:39 [ELVMAX] C:Header Len: '237' Address: '00B60B' Serial: 'IEQ0113715'
2013/11/11 20:57:39 [ELVMAX] CASE 0 Data: ��
�IEQ0113715
@���������������������
@A���������������������http://www.max-portal.elv.de:80/cubeCET
2013/11/11 20:57:39 [ELVMAX] CubeURL: 'http://www.max-portal.elv.de:80/cube'
2013/11/11 20:57:39 [ELVMAX] Received: C:02924d,0gKSTQEBFP9JRVEwNTA4MzQ3KyE9CQUYAzAM/wA8UVhjPNJY8z0gPSA9IEUgRSBFIEUgRSBFIDxRWGM80ljzPSA9ID0gRSBFIEUgRSBFIEUgPFFYYzzSWPM9ID0gPSBFIEUgRSBFIEUgRSA8UVhjPNJY8z0gPSA9IEUgRSBFIEUgRSBFIDxRWGM80ljzPSA9ID0gRSBFIEUgRSBFIEUgPFFYYzzSWPM9ID0gPSBFIEUgRSBFIEUgRSA8UVhjPNJY8z0gPSA9IEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C: C:02924d,0gKSTQEBFP9JRVEwNTA4MzQ3KyE9CQUYAzAM/wA8UVhjPNJY8z0gPSA9IEUgRSBFIEUgRSBFIDxRWGM80ljzPSA9ID0gRSBFIEUgRSBFIEUgPFFYYzzSWPM9ID0gPSBFIEUgRSBFIEUgRSA8UVhjPNJY8z0gPSA9IEUgRSBFIEUgRSBFIDxRWGM80ljzPSA9ID0gRSBFIEUgRSBFIEUgPFFYYzzSWPM9ID0gPSBFIEUgRSBFIEUgRSA8UVhjPNJY8z0gPSA9IEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C:Header Len: '210' Address: '02924D' Serial: 'IEQ0508347'
2013/11/11 20:57:39 [ELVMAX] CASE 1
2013/11/11 20:57:39 [ELVMAX] '21.5' '16' '-1'
2013/11/11 20:57:39 [ELVMAX] Received: C:033a3c,0gM6PAEEFP9JRU4wMDQ2MzU2KB4uCQsYAzAM/wBGTk5dRthO5EcgRyBHIEUgRSBFIEUgRSBFIEZOTl1G2E7kRyBHIEcgRSBFIEUgRSBFIEUgRE5OXTzMVNhO50UgRSBFIEUgRSBFIEUgRSBETk5dPMxU2E7nRSBFIEUgRSBFIEUgRSBFIEZOTl1G2E7kRyBHIEcgRSBFIEUgRSBFIEUgRE5OXTzMVNhO50UgRSBFIEUgRSBFIEUgRSBETk5dPMxU2E7nRSBFIEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C: C:033a3c,0gM6PAEEFP9JRU4wMDQ2MzU2KB4uCQsYAzAM/wBGTk5dRthO5EcgRyBHIEUgRSBFIEUgRSBFIEZOTl1G2E7kRyBHIEcgRSBFIEUgRSBFIEUgRE5OXTzMVNhO50UgRSBFIEUgRSBFIEUgRSBETk5dPMxU2E7nRSBFIEUgRSBFIEUgRSBFIEZOTl1G2E7kRyBHIEcgRSBFIEUgRSBFIEUgRE5OXTzMVNhO50UgRSBFIEUgRSBFIEUgRSBETk5dPMxU2E7nRSBFIEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C:Header Len: '210' Address: '033A3C' Serial: 'IEN0046356'
2013/11/11 20:57:39 [ELVMAX] CASE 1
2013/11/11 20:57:39 [ELVMAX] '20' '15' '2'
2013/11/11 20:57:39 [ELVMAX] Received: C:028941,0gKJQQECFP9JRVEwNTAxMTc2KB4wCQUYAzAM/wBAVEhgTNhQ6kEgQSBBIEUgRSBFIEUgRSBFIEBUSGBM2FDqQSBBIEEgRSBFIEUgRSBFIEUgQFRMWjzPVNtM6kEgQSBFIEUgRSBFIEUgRSBAVExaPM9U20zqQSBBIEUgRSBFIEUgRSBFIEBUSGBM2FDqQSBBIEEgRSBFIEUgRSBFIEUgQFRMWjzPVNtM6kEgQSBFIEUgRSBFIEUgRSBAVExaPM9U20zqQSBBIEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C: C:028941,0gKJQQECFP9JRVEwNTAxMTc2KB4wCQUYAzAM/wBAVEhgTNhQ6kEgQSBBIEUgRSBFIEUgRSBFIEBUSGBM2FDqQSBBIEEgRSBFIEUgRSBFIEUgQFRMWjzPVNtM6kEgQSBFIEUgRSBFIEUgRSBAVExaPM9U20zqQSBBIEUgRSBFIEUgRSBFIEBUSGBM2FDqQSBBIEEgRSBFIEUgRSBFIEUgQFRMWjzPVNtM6kEgQSBFIEUgRSBFIEUgRSBAVExaPM9U20zqQSBBIEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C:Header Len: '210' Address: '028941' Serial: 'IEQ0501176'
2013/11/11 20:57:39 [ELVMAX] CASE 1
2013/11/11 20:57:39 [ELVMAX] '20' '15' '-1'
2013/11/11 20:57:39 [ELVMAX] Received: C:02926c,0gKSbAEDFP9JRVEwNTA4MzcyKyEwCQcYAzAM/wAUYEhsUH4xAlELFSAVIEUgRSBFIEUgRSBFIBRgSGxQfjECUQsVIBUgRSBFIEUgRSBFIEUgFGBIbFB+MQJRCxUgFSBFIEUgRSBFIEUgRSAUYEhsUH4xAlELFSAVIEUgRSBFIEUgRSBFIBRgSGxQfjECUQsVIBUgRSBFIEUgRSBFIEUgFGBIbFB+MQJRCxUgFSBFIEUgRSBFIEUgRSAUYEhsUH4xAlELFSAVIEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C: C:02926c,0gKSbAEDFP9JRVEwNTA4MzcyKyEwCQcYAzAM/wAUYEhsUH4xAlELFSAVIEUgRSBFIEUgRSBFIBRgSGxQfjECUQsVIBUgRSBFIEUgRSBFIEUgFGBIbFB+MQJRCxUgFSBFIEUgRSBFIEUgRSAUYEhsUH4xAlELFSAVIEUgRSBFIEUgRSBFIBRgSGxQfjECUQsVIBUgRSBFIEUgRSBFIEUgFGBIbFB+MQJRCxUgFSBFIEUgRSBFIEUgRSAUYEhsUH4xAlELFSAVIEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C:Header Len: '210' Address: '02926C' Serial: 'IEQ0508372'
2013/11/11 20:57:39 [ELVMAX] CASE 1
2013/11/11 20:57:39 [ELVMAX] '21.5' '16' '0'
2013/11/11 20:57:39 [ELVMAX] Received: C:0337b8,0gM3uAEFFP9JRU4wMDQ1NzQ0LBwwCQsYA3AM/wA85FEIPSA9ID0gPSA9IEUgRSBFIEUgRSBFIDzkUQg9ID0gPSA9ID0gRSBFIEUgRSBFIEUgPORRCD0gPSA9ID0gPSBFIEUgRSBFIEUgRSA85FEIPSA9ID0gPSA9IEUgRSBFIEUgRSBFIDzkUQg9ID0gPSA9ID0gRSBFIEUgRSBFIEUgPORRCD0gPSA9ID0gPSBFIEUgRSBFIEUgRSA85FEIPSA9ID0gPSA9IEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C: C:0337b8,0gM3uAEFFP9JRU4wMDQ1NzQ0LBwwCQsYA3AM/wA85FEIPSA9ID0gPSA9IEUgRSBFIEUgRSBFIDzkUQg9ID0gPSA9ID0gRSBFIEUgRSBFIEUgPORRCD0gPSA9ID0gPSBFIEUgRSBFIEUgRSA85FEIPSA9ID0gPSA9IEUgRSBFIEUgRSBFIDzkUQg9ID0gPSA9ID0gRSBFIEUgRSBFIEUgPORRCD0gPSA9ID0gPSBFIEUgRSBFIEUgRSA85FEIPSA9ID0gPSA9IEUgRSBFIEUgRSBFIA==
2013/11/11 20:57:39 [ELVMAX] C:Header Len: '210' Address: '0337B8' Serial: 'IEN0045744'
2013/11/11 20:57:39 [ELVMAX] CASE 1
2013/11/11 20:57:39 [ELVMAX] '22' '14' '2'
2013/11/11 20:57:39 [ELVMAX] Received: L:CwKSTQkSGAkeALgACwM6PAkSGQAkAMYACwKJQQkSGAAgAL0ACwKSbAkSGAAYAKsACwM3uAkSGAkoAMwA
2013/11/11 20:57:39 [ELVMAX] L:CwKSTQkSGAkeALgACwM6PAkSGQAkAMYACwKJQQkSGAAgAL0ACwKSbAkSGAAYAKsACwM3uAkSGAkoAMwA
2013/11/11 20:57:39 [ELVMAX] L: (decoded) 0B02924D091218091E00B8000B033A3C091219002400C6000B028941091218002000BD000B02926C091218001800AB000B0337B8091218092800CC00
2013/11/11 20:57:39 [ELVMAX] len: 11 Deviceadd: 02924D
2013/11/11 20:57:39 [ELVMAX] len: 9 Deviceadd: 1E00B8
2013/11/11 20:57:39 [ELVMAX] len: 58 Deviceadd: 3C0912
2013/11/11 20:57:39 [ELVMAX] len: 0 Deviceadd: 0B0289
2013/11/11 20:57:39 [ELVMAX] len: 24 Deviceadd: 002000

RE: Feature Request: ELV MAX! support - Added by sj3fk3 5 months ago

p.s.
Ron has one somewhere he said, maybe you can borrow it from him? ;-)

RE: Feature Request: ELV MAX! support - Added by Alexie 5 months ago

I first need to check out the links ;) to understand the packets going in/out, will put it on my busy/todo list ;)

RE: Feature Request: ELV MAX! support - Added by Alexie 5 months ago

BTW: i still need to have a look into the JeeLabs decoding first.

RE: Feature Request: ELV MAX! support - Added by wwolkers 5 months ago

I need to continue on this one. I ran into the problem of what values to store in the database for ELV Max! since only 4 values can be stored, so I got sidetracked doing devicevalues support for DomotiGa.

Unfortunately I won't be able to dig into this one soon, since I'll be out of the country for a while.
Once I'm back, this will be high on my list of things to do though.

RE: Feature Request: ELV MAX! support - Added by Alexie 5 months ago

Thanks Wouter ... Also if we can decode the fields (and log them in the debug), we can work in parallel on the devicevalue support ;)

RE: Feature Request: ELV MAX! support - Added by sj3fk3 5 months ago

Yes, looks like those are the exact same devices.

RE: Feature Request: ELV MAX! support - Added by jteeuw 4 months ago

Hi ,

Is there any news about the support of the MAX! system?

RE: Feature Request: ELV MAX! support - Added by wwolkers 4 months ago

No news unfortunately. I've been busy with work mostly, not DomotiGa unfortunately.
I don't even have the ELV Max! connected at the moment after redoing my networkcabling, need to get a new switch.

I do still plan to finish this one though

RE: Feature Request: ELV MAX! support - Added by jteeuw 3 months ago

@ wwolkers

If i can help any way or do some testing , I’m glad to help

RE: Feature Request: ELV MAX! support - Added by jpl about 1 month ago

Hi all,

I got myself some ELV/EQ3Max! elements (cube/wallthermostats/radiatorthermostats). Very cool stuff.

Now I want to be able to turn on/off my central heating boiler in case one of the rooms requires heating. I want to use Domotiga for that.

However, for that I at least need to read out some of the values of the ELV/EQ3Max! ratiator thermostats.

As I understand in Domotiga device values for ELV/EQ3Max! devices are presently limited, because of the former restriction of 4 values for each device. Presently only the devicename and serialnumber is shown in Domotiga for eacgh device.

As I further understand, Domotiga now allows for unlimited number of values for each device.

It appears that the CELVMAX.class file allready decodes all the values provided by the ELV/EQ3-max! radiator thermostats and puts them in variables:

sComfortTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
iEcoTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
iMaxSetpointTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
iMinsetpointTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
itempOffset = Asc(Mid(sDecoded, iPos, 1)) / 2 - 3.5
iWindowOpenTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
iWindowOpenDuration = Hex$(Asc(Mid(sDecoded, iPos, 1)))
iBoost = Asc(Mid(sDecoded, iPos, 1))
iBoostDuration = Lsr(iBoost, 5) ' 3 MSB
iBoostValue = iBoost And 31
iDecalc = Asc(Mid(sDecoded, iPos, 1))
iDecalcDay = Lsr(iDecalc, 5)
iDecalcTime = iDecalc And 31
iMaximumValveSetting = Asc(Mid(sDecoded, iPos, 1)) * (100 / 255)
iValveOffset = Asc(Mid(sDecoded, iPos, 1)) * (100 / 255)

Some of these variables are put in the debug log:

If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "'" & sComfortTemp & "' '" & iEcoTemp & "' '" & itempOffset & "'")

However, none of these variables is currently written to device values.

It appears that, if indeed unlimited device values are presently allowed, it should be possible to write the variable to device values, such that the values can be used.

I am no programming expert though...

RE: Feature Request: ELV MAX! support - Added by jpl 2 days ago

Hi All,

I started to go trough the presently available plugin to see whether I can take the ELV/EQ3Max project a little further.

As a first step I would like to bring the project to a point that all information in the Cube is made available in Domotiga via the values of the respective devices.

I managed with some small changes to the code to add "valve position", and "set temperature" as values to my ELV/EQ3 radiator thermostats and to add "set temperature" and "actual temperature" as values to my ELV/EQ3 wall thermostats. For me to be able to have these values in Domotiga helps to control my central heating system on the basis of heating demand.

The values are currently based on information received from the Cube after initial contact.

To update the values the cube has to be polled. I understand from blogs on the internet that to get a device status update I have to send the l: command to the Cube, followed by a CR and LF.

Presently there is a subroutine for sending commands, but there are no subroutines for polling. I tried to implement polling in the plugin, but apparently my understanding of Gambas3 is not enough to get this to work.

Furthermore, I am not sure whether the send command subroutine is working.

Is there anybody who can help to implement polling into this plugin?

Greets,

JPL

RE: Feature Request: ELV MAX! support - Added by Alexie 2 days ago

Excellent you are going to work on the ELV MAX module.

To make a polling routine, you need to do the following:
- Define a Timer
- Configure the Timer (start & msec)
- Create sub routine <timername>_Timer, where you can do your poll

See e.g. the following code:
http://www.domotiga.nl/projects/domotiga/repository/revisions/master/entry/DomotiGa3/.src/CPing.class

The variable is:

Public tPing As Timer

Configure the timer:

  tPing = New Timer As "tPing" 
  tPing.Delay = $iPollTime * 1000 ' multiply for seconds
  tPing.Start

Create timer sub routine:

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' gets called at each timer event
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub tPing_Timer()
  CheckPing() <= do your magic here
End

RE: Feature Request: ELV MAX! support - Added by jpl about 22 hours ago

Hi Alexi,

I tried to implement the polling routine, but can't get it to work. I must have made some implementation mistake.

Hereinbelow you find my presently amended plugin

Can you help me get the timer working?

Regards,

JPL

' Gambas class file

' Description:
' CELVMAX.class
' Support for ELV MAX!.

' Development Status:
' Beta. Needs Testing.

' DomotiGa - an open source home automation program.
' Copyright (C) Ron Klinkien, The Netherlands.

' This module was written by Wouter Wolkers in 2012.

' Read file called COPYING for license details.

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Module/Class specific variables
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public PluginName As String = "ELVMAX" 
Public PluginType As Integer = Plugin.Type_Class
Public PluginFriendlyName As String = "ELV MAX!" 
Public PluginVersion As String = "1.00" 
Public PluginAuthor As String = "Wouter Wolkers" 
Public PluginProtocols As String[]
Public PluginMaxInstances As Integer = 1

Public KeyName As String
Public LogLabel As String = "[ELVMAX] " 
Public Instance As Integer
Public IsRunning As Boolean
Public ErrorText As String
Public ErrorWhere As String

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Private Variables
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Private $bEnabled As Boolean
Private $sTCPHost As String
Private $iTCPPort As Integer
Private $bELVMAXDebug As Boolean

Public hELVMAX As New Socket
Public sBuffer As String

Public iDeviceCount As Integer
' variable for timer 'jpl
Public tELVMax As Timer 'jpl

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Mandatory subroutine for each Module/Class to initialize:
' - The cPlugin[x].Settings are copied into local variables
' - Port/Connection will be started (any errors caught)
' - Any other code per Class
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub StartPlugin(cPl As CPluginEntry)

  KeyName = cPl.KeyName
  Instance = cPl.Instance
  If Instance <> 1 Then LogLabel = Replace(LogLabel, "] ", "#" & Instance & "] ")

  ' Copy configuration items locally
  $bEnabled = cPl.Settings["enabled"]
  $sTCPHost = cPl.Settings["tcphost"]
  $iTCPPort = cPl.Settings["tcpport"]
  $bELVMAXDebug = cPl.Settings["debug"]

  ' Connect/Initialize connection
  ConnectTCP()

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Mandatory sub for EACH Module/Class to stop
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub StopPlugin()

  Try Disconnect()

End

' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' set timer 'jpl
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Public Sub Run()

  ' start poll Timer For ELVMAX
  tELVMax = New Timer As "tELVMax" 
  tELVMax.Delay = 10000 ' $iPollTime * 1000 ' multiply for seconds
  tELVMax.Start

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' gets called at each timer event 'jpl
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub tELVMax_Timer()

  SendCmd("l:")
  ELVMax_Read()

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Converts a character to the relevant index
' See http://en.wikipedia.org/wiki/Base64
' This is used in decoding
Private Function charToIndex(char As String) As Integer

  If char >= "A" And char <= "Z" Then
    Return Asc(char) - 65
  Else If char >= "a" And char <= "z" Then
    Return Asc(char) - 71
  Else If char >= "0" And char <= "9" Then
    Return Asc(char) + 4
  Else If char = "+" Then
    Return 62
  Else If char = "/" Then
    Return 63
  Endif
  Return -1

End

' Converts a 'unit' of base64 (4 characters) into a Byte array (contains up to 3 bytes)
Private Function fourEncToBytes(fourEnc As String) As String

  Dim idx, fourVal, aByteVal, charIdx As Integer
  Dim aChar, threeChars As String

  ' Ensure our value accumulator is always zero to start
  fourVal = 0
  ' Knock off the padding '=' signs from the end if required
  Do While Right$(fourEnc, 1) = "=" 
    fourEnc = Right$(fourEnc, -1)
  Loop
  ' For each character in the base64 unit (now may be less than 4 characters)
  For idx = 1 To Len(fourEnc)
    achar = Mid$(fourEnc, idx, 1)
    ' Use the conversion table for base64 (see for example http://en.wikipedia.org/wiki/Base64 and look for...  The Base64 index table:)
    charIdx = charToIndex(aChar)
    ' Accumulate the values, multiplying by 64 to the power 3 for the first character index, 64 to the power 2 for the second character index etc.
    fourVal = fourVal + (charIdx * 64 ^ (4 - idx))
  Next
  ' Ensure our result is always empty to start with
  For idx = 1 To Len(fourEnc) - 1
    threeChars &= Chr$(fourVal / (256 ^ (3 - idx)))
    aByteVal = Int(fourVal / (256 ^ (3 - idx)))
    fourVal = fourVal - aByteVal * (256 ^ (3 - idx))
  Next
  Return threeChars

End

Private Function Base64Decode(sBase64 As String) As String

  Dim idx As Integer
  Dim aChar, fourEnc, result, threeChars As String

  ' Step through the complete base64 string character by character
  For idx = 1 To Len(sBase64)
    aChar = Mid$(sBase64, idx, 1)
    ' Only include what is not whitespace
    If Asc(aChar) > 31 Then
      ' Build up a 4-character string
      fourEnc = fourEnc & aChar
      ' Once we have 4 characters in our string (it can include padding consisting of '=' signs)
      If Len(fourEnc) = 4 Then
        ' Convert a unit (4 characters) of base64 to ascii (3 characters)
        threeChars = fourEncToBytes(fourEnc)
        result &= threeChars
        ' Be sure to clear out fourEnc for the next unit of base64
        fourEnc = "" 
      Endif
    Endif
  Next
  Return result

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' connect to the tcp host:port
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Function ConnectTCP() As Boolean

  ' try to close the connection
  Try hELVMAX.Close

  ' get a new one
  hELVMAX = New Socket As "ELVMAX" 
  hELVMAX.Connect($sTCPHost, $iTCPPort)

  ' Write to main logfile we are trying to connect
  Main.WriteLog(LogLabel & PluginFriendlyName & " TCP interface connecting to Server " & $sTCPHost & ":" & $iTCPPort)

  ' Don't set IsRunning=True, this has to be done in _Ready 

  ' All went ok
  Return True

Catch ' some errors
  Main.WriteLog(LogLabel & "ERROR: " & PluginFriendlyName & " TCP interface FAILED to connect to Server " & $sTCPHost & ":" & $iTCPPort)
  Main.WriteLog(LogLabel & "ERROR: " & Error.Text)
  IsRunning = False
  ErrorText = Error.Text
  ErrorWhere = Error.Where

  Return False

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' socket is connected
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub ELVMAX_Ready()

  Main.WriteLog(LogLabel & "TCP interface connected.")
  IsRunning = True

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' socket is closed
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub ELVMAX_Closed()

  Main.WriteLog(LogLabel & "TCP socket closed.")

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' disconnect from the tcp host
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Function Disconnect() As Boolean

  ' try to close the connection
  Try hELVMAX.Close

  Main.WriteLog(LogLabel & PluginFriendlyName & " TCP Server closed.")

  ' all ok
  Return True

Finally
  IsRunning = False
  ErrorText = "" 
  ErrorWhere = "" 

Catch
  Main.WriteLog(LogLabel & "ERROR: " & Error.Text)
  Return False

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' error while connected/connecting to host
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub ELVMAX_Error()

  Dim sMsg As String

  sMsg = Log.Text_TCP_Error(Last.Status, $sTCPHost)
  Main.WriteLog(LogLabel & "ERROR: " & sMsg)

  IsRunning = False
  ErrorText = sMsg

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' send command to tcp socket
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub SendCmd(sCmd As String)

  If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Sending '" & sCmd)
  Try Write #hELVMAX, sCmd & Chr$(13), Len(sCmd) + 1
  If Error Then Main.WriteDebugLog(LogLabel & "Error writing data to the TCP port! -> " & ERROR.Text)

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' send command to device
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub SendCommand(sAddress As String, sValue As String)
  Select Case LCase(sValue)
    Case "on" 
      ' SendCmd("PWSTANDBY")
    Case "off" 
      ' SendCmd("PWON")
    Case Else
      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Unsupported command received: " & sValue)
  End Select

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' data received on tcp socket
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Public Sub ELVMax_Read()

  Dim sData As String

  Try sData = Read #hELVMAX, 1
  If Error Then Main.WriteDebugLog(LogLabel & "Error reading data from the TCP port! -> " & Error.Text)
  If sData = Chr$(13) Then ' buffer until linefeed then parse
    If Len(sBuffer) > 1 Then ParseLine(sBuffer)
    sBuffer = Null
  Else
    sBuffer &= sData
  Endif

End

Public Sub ELVMAX_Found()

  Log.Plugin_DNS_Found(LogLabel, $sTCPHost)

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' parse received data
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Private Sub ParseLine(sStr As String)

  Dim sMaincat, sDecoded, sMessage, sDeviceSerial, sComfortTemp, sRoomName, sRoomAddress, sDeviceType, sDeviceAddress, sSerialNumber, sDeviceName, sCubeURL As String
  Dim aArray As String[]
  Dim iDeviceId, iRooms, iPos, iDataLength, iEcoTemp, iMaxSetpointTemp, iMinSetpointTemp, iTempOffset, iWindowOpenTemp, iWindowOpenDuration, iBoost, iRoomNumber As Integer
  Dim i, iNameLength, iRoomID, iPortalEnabled, Name_Length, Mi As Integer
  Dim iBoostDuration, iBoostValue, iDecalc, iDecalcDay, iDecalcTime, iMaximumValveSetting, iValveOffset, iData1, iData2, iTemperature, iValvePosition, iTimeUntil As Integer
  Dim iSetTemp, iActualTemp As Float 'jw

  sMaincat = sStr

  ' Remove all CR and LF
  If Left(sMaincat, 1) = Chr$(10) Then sMaincat = Right(sMaincat, -1)
  If Left(sMaincat, 1) = Chr$(13) Then sMaincat = Right(sMaincat, -1)
  sMessage = Left(sMaincat, 2)
  If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Received: " & sMaincat)

  Select Case sMessage

    Case "H:" 
      ' Hello
      aArray = Split(Right$(sMaincat, -2))

      Main.WriteDebugLog(LogLabel & "SerialNumber: '" & aArray[0] & "'")
      '     $data["sys"]["Date"] = hexdec(Mid$(aArray[7],4,2)).".".hexdec(Mid$(aArray[7],2,2)).".".hexdec(Mid$(aArray[7],0,2));
      '     $data["sys"]["Time"] = hexdec(substr($arr2[8],0,2)).":".hexdec(substr($arr2[8],2,2));
      '     $data["sys"]["Timestamp"] = mktime(hexdec(substr($arr2[8],0,2)),hexdec(substr($arr2[8],2,2)),0,hexdec(substr($arr2[7],2,2)),hexdec(substr($arr2[7],4,2));
      ' try to find device with address, and correct interface type.
      iDeviceId = Devices.Find(Instance, aArray[1], Devices.FindInterface("ELV MAX! Interface"), "ELVCube")
      ' if found then update it's value
      If iDeviceId Then
        Devices.ValueUpdateExt(iDeviceId, 1, aArray[0])
        Devices.ValueUpdateExt(iDeviceId, 2, aArray[2])
      Endif

    Case "M:" 
      ' Metadata: Rooms, Device names
      aArray = Split(Right$(sMaincat, -2))
      iPos = 1 ' Start reading at 1

      sDecoded = Base64Decode(aArray[2])
      ' Print the decoded string in printable chars. Abuse sDeviceType to save some memory on vars :)
      For i = 1 To Len(sDecoded)
        sDeviceType &= Hex$(Asc(Mid$(sDecoded, i, 1)), 2)
      Next
      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "M: " & sDeviceType)
      sDeviceType = "" 

      For Mi = 1 To Len(sDecoded)
        sRoomName &= Hex$(Asc(Mid$(sDecoded, Mi, 1)), 2)
      Next
      Main.WriteDebugLog(LogLabel & sRoomName)

      iPos += 2 ' first 2 bytes are unknown still.
      iRooms = Asc(Mid(sDecoded, iPos, 1))
      iPos += 1
      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Found '" & iRooms & "' rooms.")

      For Mi = 1 To iRooms
        iRoomNumber = Asc(Mid(sDecoded, iPos, 2))
        iPos += 1
        Name_Length = Asc(Mid(sDecoded, iPos, 2))
        iPos += 1
        sRoomName = Mid(sDecoded, iPos, Name_Length)
        iPos += Name_Length
        sRoomAddress = "" 
        For i = iPos To iPos + 2 ' read 3 bytes
          sRoomAddress &= Hex$(Asc(Mid(sDecoded, i, 1)), 2)
        Next
        iPos += 3
        If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "M: #rooms: '" & CStr(iRooms) & "' RoomNumber: '" & CStr(iRoomNumber) & "' RoomName: '" & sRoomName & "' 1stAddressInRoom: '" & sRoomAddress & "'")
      Next

      iDeviceCount = Asc(Mid(sDecoded, iPos, 1))
      iPos += 1

      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Found " & iDeviceCount & " devices.")

      For Mi = 1 To iDeviceCount
        sDeviceType = Asc(Mid(sDecoded, iPos, 1))
        iPos += 1
        sDeviceAddress = "" 
        For i = iPos To iPos + 2 ' read 3 bytes
          sDeviceAddress &= Hex$(Asc(Mid(sDecoded, i, 1)), 2)
        Next
        iPos += 3
        For i = iPos To iPos + 9 ' read 10 bytes
          sSerialNumber = Asc(Mid(sDecoded, i, 1))
        Next
        iPos += 10
        iNameLength = Asc(Mid(sDecoded, iPos, 1))
        iPos += 1
        sDeviceName = Mid(sDecoded, iPos, iNameLength)
        iPos += iNameLength
        iRoomID = Asc(Mid(sDecoded, iPos, 1))
        iPos += 1
        If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "M: DeviceType: '" & sDeviceType & "' DeviceAddress: '" & sDeviceAddress & "' DeviceName: '" & sDeviceName & "'")

        ' Update this device info in DomotiGa
        Select Case sDeviceType
          Case "0" 
            ' Cube
            sDeviceType = "ELVCube" 
          Case "1" 
            ' Radiator thermostat
            sDeviceType = "ELVRadThermo" 
          Case "4" 
            ' Door/window sensor
            sDeviceType = "ELVDOOR" 
          Case Else
            sDeviceType = "Unknown" 
            Main.WriteDebugLog(LogLabel & "Unknown device type '" & sDeviceType & "' Please report this to support.")
        End Select
        ' try to find device with address, and correct interface type.
        iDeviceId = Devices.Find(Instance, sDeviceAddress, Devices.FindInterface("ELV MAX! Interface"), sDeviceType)
        ' if found then update it's value
        If iDeviceId Then
          Devices.ValueUpdateExt(iDeviceId, 1, sDeviceName)
          Devices.ValueUpdateExt(iDeviceId, 2, sSerialNumber)
        Endif
      Next

    Case "C:" 
      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "C: " & sMaincat)

      aArray = Split(Right$(sMaincat, -2))

      sDeviceAddress = aArray[0]
      sDecoded = Base64Decode(aArray[1])

      iPos = 1   ' Start reading at 1
      iDataLength = Asc(Mid(sDecoded, iPos, 1))
      iPos += 1
      For i = iPos To iPos + 2             ' read 3 bytes, RF Address of device
        sRoomAddress &= Hex$(Asc(Mid(sDecoded, i, 1)), 2)
      Next
      iPos += 3
      sDeviceType = Asc(Mid(sDecoded, iPos, 1))    ' Device Type
      iPos += 1
      iPos += 3    ' Unknown bytes
      For i = iPos To iPos + 9             ' read 10 bytes, Serial of device
        sDeviceSerial = sDeviceSerial & Mid(sDecoded, i, 1)
      Next
      iPos += 10
      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "C:Header Len: '" & iDataLength & "' Address: '" & sRoomAddress & "' Serial: '" & sDeviceSerial & "'")

      Select Case sDeviceType
        Case "0" ' Cube
          If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "CASE 0 Data: " & sDecoded & "'")
          iPortalEnabled = Asc(Mid(sDecoded, iPos, 1))
          iPos += 1
          iPos += 66 ' Unknown
          For i = iPos To iPos + 36             ' read 10 bytes, URL for portal
            sCubeURL &= Mid(sDecoded, i, 1)
          Next
          If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "CubeURL: '" & sCubeURL & "'")

        Case "1" ' Radiator Thermostat
          If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "CASE 1")
          sComfortTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
          iPos += 1
          iEcoTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
          iPos += 1
          iMaxSetpointTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
          iPos += 1
          iMinsetpointTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
          iPos += 1
          itempOffset = Asc(Mid(sDecoded, iPos, 1)) / 2 - 3.5
          iPos += 1
          iWindowOpenTemp = Asc(Mid(sDecoded, iPos, 1)) / 2
          iPos += 1
          iWindowOpenDuration = Hex$(Asc(Mid(sDecoded, iPos, 1)))
          iPos += 1
          iBoost = Asc(Mid(sDecoded, iPos, 1))
          iPos += 1
          ' Boost Duration and Boost Valve Value
          ' The 3 MSB bits gives the duration, the 5 LSB bits the Valve Value%.
          ' Duration: With 3 bits, the possible values (Dec) are 0 to 7, 0 is not used.
          ' The duration in Minutes is: if Dec value = 7, then 30 minutes, else Dec value * 5 minutes
          ' Valve Value: dec value 5 LSB bits * 5 gives Valve Value in %
          iBoostDuration = Lsr(iBoost, 5) ' 3 MSB
          iBoostValue = iBoost And 31
          iPos += 1
          ' Decalcification: Day of week and Time
          ' In bits: DDDHHHHH
          ' The three most significant bits (MSB) are presenting the day, Saturday = 1, Friday = 7
          ' The five least significant bits (LSB) are presenting the time (in hours)
          iDecalc = Asc(Mid(sDecoded, iPos, 1))
          iDecalcDay = Lsr(iDecalc, 5)
          iDecalcTime = iDecalc And 31
          iPos += 1
          iMaximumValveSetting = Asc(Mid(sDecoded, iPos, 1)) * (100 / 255)
          iPos += 1
          iValveOffset = Asc(Mid(sDecoded, iPos, 1)) * (100 / 255)
          iPos += 1

          If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "'" & sComfortTemp & "' '" & iEcoTemp & "' '" & itempOffset & "'")

        Case "4" ' Door / window sensor
          If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & " Found C: for Door / Window sensor. No further data.")

        Case Else
          Main.WriteDebugLog(LogLabel & "Unknown device type '" & sDeviceType & "', please report this to support.")
      End Select

    Case "L:" 
      Main.WriteDebugLog(LogLabel & "L:" & Right$(sMaincat, -2))
      sDecoded = Base64Decode(Right$(sMaincat, -2))

      ' Print the decoded string in printable chars. Abuse sDeviceType to save some memory on vars :)
      For i = 1 To Len(sDecoded)
        sDeviceType &= Hex$(Asc(Mid$(sDecoded, i, 1)), 2)
      Next
      Main.WriteDebugLog(LogLabel & "L: (decoded) " & sDeviceType)
      sDeviceType = "" 

      iPos = 1

      For iPortalEnabled = 1 To iDeviceCount
        iDataLength = Asc(Mid(sDecoded, iPos, 1))
        iPos += 1
        For i = iPos To iPos + 2              ' read 3 bytes
          sDeviceAddress &= Hex$(Asc(Mid(sDecoded, i, 1)), 2)
        Next
        iPos += 3
        iPos += 1 ' Unknown byte
        iData1 = Asc(Mid(sDecoded, iPos, 1))
        iPos += 1
        iData2 = Asc(Mid(sDecoded, iPos, 1))
        iPos += 1
        ' If iDataLength > 11 Then
        ' In case of radiator thermostat there are 11 bytes 
        If iDataLength = 11 Then 'jpl
         iValvePosition = Asc(Mid(sDecoded, iPos, 1))
         iPos += 1
         'iTemperature = Asc(Mid(sDecoded, iPos, 1)) / 2
         iSetTemp = Asc(Mid(sDecoded, iPos, 1)) / 2 'jpl
         iPos += 1
         ' DateUntil  ' 2 bytes
         iPos += 2
         iTimeUntil = Asc(Mid(sDecoded, iPos, 1)) * 0.5
         ' Update this device info in DomotiGa ' jpl
         ' try to find device with address, and correct interface type. 'jpl
         iDeviceId = Devices.Find(Instance, sDeviceAddress, Devices.FindInterface("ELV MAX! Interface")) 'jpl
         ' if found then update it's value 'jpl
         If iDeviceId Then 'jpl
           Devices.ValueUpdateExt(iDeviceId, 3, iSetTemp) 'jpl
           Devices.ValueUpdateExt(iDeviceId, 4, iValvePosition) 'jpl
         Endif 'jpl
        Endif
        ' In case of Wallthermostat there are 12 bytes 
        If iDataLength = 12 Then ' jpl
         iPos += 1 'jpl
         iSetTemp = Round((Asc(Mid(sDecoded, iPos, 1)) / 2), -1) 'jpl
         iPos += 1 'jpl
         iPos += 2 'jpl
         iPos += 1 'jpl
         iActualTemp = Round((Asc(Mid(sDecoded, iPos, 1)) / 10), -1) 'jpl
         ' Update this device info in DomotiGa ' jpl
         ' try to find device with address, and correct interface type. 'jpl
         iDeviceId = Devices.Find(Instance, sDeviceAddress, Devices.FindInterface("ELV MAX! Interface")) 'jpl
         ' if found then update it's value 'jpl
         If iDeviceId Then 'jpl
           Devices.ValueUpdateExt(iDeviceId, 3, iSetTemp) 'jpl
           Devices.ValueUpdateExt(iDeviceId, 4, iActualTemp) 'jpl
         Endif ' jpl
        Endif 'jpl
        iPos += 1 'jpl
        Main.WriteDebugLog(LogLabel & "len: " & iDataLength & " Deviceadd: " & sDeviceAddress)
        ' Do something with the values here, before we clear them.
        sDeviceAddress = "" 
      Next

    Case Else
      If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Not implemented yet!")
  End Select

End

'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' save received values
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Private Sub ELVMaxSetValue(sItem As String, vValue As Variant)

  Main.SetGlobalVar("ELVMax_" & sItem, vValue)
  If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Received Item: " & sItem & " Value: " & vValue)

End

RE: Feature Request: ELV MAX! support - Added by wwolkers about 13 hours ago

From a quick look the sendcmd function is not correct.
It should send CR+LF at the end of each command, but it's only sending chr(13)

try this one:

Public Sub SendCmd(sCmd As String)

If $bELVMAXDebug Then Main.WriteDebugLog(LogLabel & "Sending '" & sCmd)
Try Write #hELVMAX, sCmd & Chr$(13) & Chr$(10), Len(sCmd) + 1
If Error Then Main.WriteDebugLog(LogLabel & "Error writing data to the TCP port! -> " & ERROR.Text)

End

1 2 (1-25/28)