zxdavb / ramses_rf

An interface for the RAMSES RF protocol, as used by Honeywell-compatible HVAC & CH/DHW systems.
MIT License
62 stars 16 forks source link

31da struct resolved #29

Closed Ierlandfan closed 3 years ago

Ierlandfan commented 3 years ago

Hi, can you help me with the following:

I found a structure (Originally it's visual basic) for the 31DA type: In this case it's from Itho Daalderop .

2021-05-23T10:19:11.077679 070  I --- 37:054619 --:------ 37:054619 31DA 029 00EF007FFFEFEF7FFF7FFF7FFF7FFFF800EF8301000000EFEF7FFF7FFF

Structure is:

data.data[0] > 200
data.data[0]    
else
decimal num = new decimal((double)(int)data.data[0] / 2.0);

data_length > 0
data.data[1]

data_length > 1
int num2 = unchecked((int)data.data[2]) * 256 + unchecked((int)data.data[3]) //num2 <= 32766

data_length > 3
int num3 = data.data[4]; //num3 needs to be <=200

data_length > 4
int num4 = data.data[5]; //num4 needs to be <=200

data_length > 5
int num5 = unchecked((int)data.data[6]) * 256 + unchecked((int)data.data[7]) //num5 needs to be <= 32766

data_length > 7
int num6 = unchecked((int)data.data[8]) * 256 + unchecked((int)data.data[9]); //num6 needs to be <= 32766

data_length > 9
int num7 = unchecked((int)data.data[10]) * 256 + unchecked((int)data.data[11]); num6 needs to be <= 32766
format: (double)num7 / 100.0

data_length > 11
int num8 = unchecked((int)data.data[12]) * 256 + unchecked((int)data.data[13]); num8 needs to be <= 32766
format: (double)num8

data_length > 13
int val = unchecked((int)data.data[14]) * 256 + unchecked((int)data.data[15]);

data_length > 15
int num9 = data.data[16] 
format: (double)num9 / 2.0

data_length > 16
int num10 = data.data[17];
format: num10 & 0x1F //don't know

data_length > 17
int num11 = data.data[18] //num11 need to be <=200
format: (double)num11 / 2.0 

data_length > 18
int num12 = data.data[19]; need to be <=200
format: (double)num12 / 2.0

data_length > 19
int num13 = unchecked((int)data.data[20]) * 256 + unchecked((int)data.data[21]);

data_length > 21
int num14 = data.data[22]; needs to be <=200
format: (double)num14 / 2.0

data_length > 22
int num15 = data.data[23];
format: (double)num15 / 2.0

data_length > 23
int num16 = unchecked((int)data.data[24]) * 256 + unchecked((int)data.data[25]); //Need to be <=32766
format: (double)num16 / 100.0 

data_length > 25
int num17 = unchecked((int)data.data[26]) * 256 + unchecked((int)data.data[27]); //Need to be <=32766
format: (double)num17 / 100.0

Can we add this to the parser?

zxdavb commented 3 years ago

Thanks for this. Is quite useful but the main issue is what do the fields mean?

Like, if a number needs to be less than 200 (0x00-0xC8, or 0xFE/FF), then it's almost certainly a percentage...

But of what? Fan Speed, Humidity, Other?

Do you have a link to this very exciting VB code?

Ierlandfan commented 3 years ago

I decompiled their service tool so if there's no problem with that (try ILspy) you can find it here: https://www.ithodaalderop.nl/nl-NL/professional/servicetool The database passwords are not hard to find

zxdavb commented 3 years ago

I am sorry - I will need more hints that that.

Ierlandfan commented 3 years ago

Some values es are references to one or more databases and subtables. There are more struc references. If you could provide the code for this struct, I will lookup their meaning and add struct code for others, I also have the command stru t and settings structs

zxdavb commented 3 years ago

If you could provide the code for this struct

I am not 100% sure what you mean - this is what I have so far, incorporating your bit, above:

    assert msg.len == 29, f"expected length 29, not {msg.len}"  # usu: I CTL-->CTL

    assert payload[2:4] in ("00", "EF"), payload[2:4]
    assert payload[4:6] in ("00", "40"), payload[4:6]
    assert payload[6:10] in ("07D0", "7FFF"), payload[6:10]
    assert payload[10:12] == "EF" or int(payload[10:12], 16) <= 100, payload[10:12]
    assert payload[12:14] == "EF", payload[12:14]
    assert payload[14:18] == "7FFF", payload[14:18]
    assert payload[18:22] == "7FFF", payload[18:22]
    assert payload[22:26] == "7FFF", payload[22:26]
    assert payload[26:30] == "7FFF", payload[26:30]
    assert payload[30:34] in ("0002", "F000", "F800", "F808", "7FFF"), payload[30:34]
    assert payload[34:36] == "EF", payload[34:36]
    assert payload[36:38] == "EF" or int(payload[36:38], 16) <= 200, payload[36:38]
    assert payload[38:40] in ("EF", "FF") or int(payload[38:40], 16) <= 200, payload[38:40]
    assert payload[40:42] in ("00", "EF", "FF"), payload[40:42]
    # assert payload[42:46] == "0000", payload[42:46]
    assert payload[46:48] in ("00", "EF"), payload[46:48]
    assert payload[48:50] == "EF", payload[48:50]
    assert payload[50:54] == "7FFF" , payload[50:54]
    assert payload[54:58] == "7FFF", payload[54:58]

    return {
        "_unknown_00": _percent(payload[2:4]),
        "unknown_12c8": _percent(payload[4:6]),  # NOTE: 12C8/payload[4:6]
        "unknown_1298": _temp(payload[6:10]),  # NOTE: 1298/payload[2:6]
        "relative_humidity": _percent(payload[10:12], precision=1),
        "_unknown_04": _percent(payload[12:14]),
        "_unknown_05": _double(payload[14:18]),
        "_unknown_06": _double(payload[18:22]),
        "_unknown_07": _double(payload[22:26], factor=100),
        "_unknown_08": _double(payload[26:30]),
        "_unknown_0x": _double(payload[30:34]),
        "_unknown_09": _percent(payload[34:36]),
        "_unknown_10": payload[36:38],  # TODO: & 0x1F ?
        FanSwitch.FAN_RATE: _percent(payload[38:40]),  # NOTE: 31D9/payload[4:6]
        "_unknown_12": _percent(payload[40:42]),
        FanSwitch.BOOST_TIMER: _double(payload[42:46]),  # NOTE: 22F3/payload[2:6]
        "_unknown_14": _percent(payload[46:48]),
        "_unknown_15": _percent(payload[48:50]),
        "_unknown_16": _double(payload[50:54], factor=100),
        "_unknown_17": _double(payload[54:58], factor=100),
    }

You can see, I currently have only:

For most values I have ever seen either EF or 7FFF - the equivalent of N/A, None or null.

Note the indexes are different; mine = (yours * 2) + 2

Ierlandfan commented 3 years ago
Traceback (most recent call last):
  File "/home/brakero1/ramses_rf/ramses_rf/message.py", line 402, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/home/brakero1/ramses_rf/ramses_rf/parsers.py", line 264, in wrapper
    result = func(*args, **kwargs)
  File "/home/brakero1/ramses_rf/ramses_rf/parsers.py", line 1629, in parser_31da
    "_unknown_00": _percent(payload[2:4]),
  File "/home/brakero1/ramses_rf/ramses_rf/parsers.py", line 322, in _percent
    assert int(value, 16) <= 200, "max value should be C8"
AssertionError: max value should be C8

Here are the values they represent:

AirQuality (%) AirQbase (...) CO2level (ppm) Indoorhumidity (%) Outdoorhumidity (%) Exhausttemperature (degrees C) SupplyTemperature (degrees C) IndoorTemperature (degrees C) OutdoorTemperature (degrees C) SpeedCap (....) BypassPos (%) FanInfo * ExhFanSpeed (%) InFanSpeed (%) RemainingTime (minutes) PostHeat (%) PreHeat (%) InFlow liter/second ExhaustFlow liter/second

Now trying to figure out what the 31D9 messages are that are sent after an 31DA.

zxdavb commented 3 years ago

I have pushed to master.

You could look for 22F1, 22F3, 3120, 31D9, 31E0, anything starting 12xx