Honeywell CH/DHW via RF - evohome, sundial, hometronics, chronotherm

@zxdavb , I submitted Hometronic setpoint adjustment broken in 0.11.0 · Issue #36 · zxdavb/ramses_rf · GitHub for you.

It’s a very nicely-written issue. Thanks.

I have a solution in mind - I’ll be implementing ‘profiles’ that will know the difference between evohome (the default) and other systems (e.g. Hometronics, RoundThermostat).

@zxdavb thanks for the great integration, I have everything up and running and I’m able to read the temperature from my Honeywell HCW80.

One thing is eluding me though: My receiver is a R6660D … Is it possible to send commands to this using evohome_cc (or ramses_rf) or do I need to upgrade to evohome for this?

Great - I would love to see some packet logs.

Yes, you can send commands to the relay (R6660D, aka HC60NG). No, you do not need to upgrade, you have all the hardware you need.

The specific mechanics how to do this are TBD, as the HC60NG will be listening only to the HCW80, but (some hints):

  • you many have to ‘replace’ the HCW80 with evohome_cc pretending to be a HCW80
  • you may be able to bind evohome_cc to the HC60NG ins addition to the HCW80 (BDR91s can do this)

OK, I think I have enough knowhow to do this given that I can see the messages coming in from my HCW80 - I also have evogateway set up which gives me some additional debug info for various messages - however, the bit I’m missing is the literal “how to send the commands”… Is there an existing API for broadcasting a given blob of data in evohome_cc, or do I need to use barebone ramses_rf and then integrate it with the former?

I’d imagine most of the logging is from ramses_rf ? - it has very extensive logging, but I’m happy to be wrong, and I’d be curious to see what he’s pulling out:

logger:
  default: warn  # prefer warn over info, avoid debug
  logs:
    homeassistant.core: debug  # to see: Event state_changed, or not
    # homeassistant.loader: info  # You are using a custom integration for evohome_cc...
    # homeassistant.setup: info  # Setting up evohome_cc

    custom_components.evohome_cc: warn  # use info for Schema =

    # ramses_rf: debug  # for engine state
    ramses_rf.message: info  # for MSGs received (incl. sent & echo'd)
    # ramses_rf.protocol: info  # for PKTs sent (not retries) & received
    # ramses_rf.transport: info  # for RF Tx & Rx

    ramses_rf.systems: debug
    ramses_rf.zones: debug
    ramses_rf.devices: debug

There is an (?) undocumented parameter for configuration.yaml:

evohome_cc:
  send_packet: true

… and a magic service called evohome_cc.send_packet will appear:

service: evohome_cc.send_packet
data:
  device_id: '01:123456'
  verb: RQ
  code: 1F09
  payload: FF

I am sorry, impersonation is not yet implemented with this service call (i.e. you can set the destination device ID, but not the source ID).

Ah!!! I should have dug further into the source! This is plenty for me to go on … I’m not sure exactly when I’ll get around to trying it all but I’ll post back with progress (or lack thereof) soon!

Any chance I can see some log files from your system - not a configuration I see often, and it may offer some insights into the protocol.

If anyone has a HB05, HB15, HB85, HB95 (external weather sensor) attached to a Hometronics system, or an older evohome system, can they PM me? I am very keen to get a packet log of re-binding the sensor.

For those with an OTB (OpenTherm Bridge, R8810, R8820), what precision are you getting expecting for the following:

  • temperature (I expect to 0.01 C)
  • percentage (I expect to 0.5% - 0.005)
  • pressure (bar)
  • relative humidity (percentage, so 0.5% ?)
  • flow (rate, L/min)
  • burner current (µA)

@zxdavb Not sure if it’s possible but is there anyway to speed up the refresh interval for the Opentherm sensors? I have currently got the scan_interval in the HA config set to 60 seconds but this doesn’t appear to be having much effect?

When I had an Opentherm Gateway connected between the Opentherm Bridge and the boiler I was getting near real-time updates.

@DanRP The SCAN_INTERVAL is how often HA polls ramses_rf. (there is a long-term plan to use callbacks instead of polling)

However, ramses_rf polls the OTB every unit time according to this code:

if msg.code == _1FD4:  # every 30s
    if msg.payload["ticker"] % 60 in (1, 3):  # effectively once every 30 mins
        self._discover(discover_flag=DISCOVER_PARAMS)
    elif msg.payload["ticker"] % 6 in (0, 2):  # twice every 3 mins
        self._discover(discover_flag=DISCOVER_STATUS)

In short, it asks for these variables every 90 seconds:

    STATUS_MSG_IDS = (
        0x00,  # ..0: "Master/Slave status flags",                          # not native
        0x11,  # .17: "Relative Modulation Level (%)",
        0x12,  # .18: "Water pressure in CH circuit (bar)",
        0x13,  # .19: "Water flow rate in DHW circuit. (L/min)",
        0x19,  # .25: "Boiler flow water temperature (°C)",
        0x1A,  # .26: "DHW temperature (°C)",
        0x1B,  # .27: "Outside temperature (°C)",  # TODO: any value here?
        0x1C,  # .28: "Return water temperature (°C)",
    )

Asking for this data more often that seems a bit egregious to me, and may exceed the RF duty cycle in any case.

If the OTB ever broadcasts this data, then ramses_rf will have the latest value at that point, rather than having to wait for the next poll.

@zxdavb Thanks David, understood.

Just started testing the OTB today. Seeing these errors appearing, not sure if they are helpful:

2021-08-14T20:15:48.984970 045 RQ --- 01:210309 10:022270 --:------ 3220 005 00808C0000 < Validation error 
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/message.py", line 454, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 276, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 1842, in parser_3220
    assert (
AssertionError: OpenTherm: Invalid data-id: 0x8C (140), msg-type = Read-Data
2021-08-14T20:15:49.013961 047 RP --- 10:022270 01:210309 --:------ 3220 005 00708C0000 < Corrupt payload: OpenTherm: 'Unknown data-id: 0x8C (140), msg-type = Unknown-DataId' (will be ignored)
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 276, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 1839, in parser_3220
    ot_type, ot_id, ot_value, ot_schema = decode_frame(payload[2:10])
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/opentherm.py", line 975, in decode_frame
    raise KeyError(
KeyError: 'Unknown data-id: 0x8C (140), msg-type = Unknown-DataId'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/message.py", line 454, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 278, in wrapper
    raise CorruptPayloadError(f"OpenTherm: {err}")
ramses_rf.exceptions.CorruptPayloadError: Corrupt payload: OpenTherm: 'Unknown data-id: 0x8C (140), msg-type = Unknown-DataId' (will be ignored)
2021-08-14 20:19:03 INFO (MainThread) [ramses_rf.message] Message( I|04:030580|2309), received at 2021-08-14 19:45:09.331170: msg has expired (0:33:53.688626, 0:30:00)
2021-08-14 20:20:03 INFO (MainThread) [ramses_rf.message] Message( I|01:210309|1F41), received at 2021-08-14 16:19:16.781307: msg has expired (4:00:46.440715, 4:00:00)
2021-08-14T20:20:50.830645 044 RQ --- 01:210309 10:022270 --:------ 3220 005 00808C0000 < Validation error 
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/message.py", line 454, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 276, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 1842, in parser_3220
    assert (
AssertionError: OpenTherm: Invalid data-id: 0x8C (140), msg-type = Read-Data
2021-08-14T20:20:50.859644 047 RP --- 10:022270 01:210309 --:------ 3220 005 00B08C0000 < Validation error 
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/message.py", line 454, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 276, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 1842, in parser_3220
    assert (
AssertionError: OpenTherm: Invalid data-id: 0x8C (140), msg-type = -reserved-
2021-08-14 20:24:03 INFO (MainThread) [ramses_rf.message] Message( I|04:033006|2309), received at 2021-08-14 19:53:55.538176: msg has expired (0:30:07.489061, 0:30:00)
2021-08-14 20:24:03 INFO (MainThread) [ramses_rf.message] Message(RP|10:022270|3220|00), received at 2021-08-14 20:18:05.766459: msg has expired (0:05:57.263935, 0:05:00)
2021-08-14 20:24:03 INFO (MainThread) [ramses_rf.message] Message(RP|10:022270|3220|1B), received at 2021-08-14 20:18:06.095488: msg has expired (0:05:56.935307, 0:05:00)
2021-08-14T20:25:52.497375 044 RQ --- 01:210309 10:022270 --:------ 3220 005 00808C0000 < Validation error 
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/message.py", line 454, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 276, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 1842, in parser_3220
    assert (
AssertionError: OpenTherm: Invalid data-id: 0x8C (140), msg-type = Read-Data
2021-08-14T20:25:52.525372 047 RP --- 10:022270 01:210309 --:------ 3220 005 00708C0000 < Corrupt payload: OpenTherm: 'Unknown data-id: 0x8C (140), msg-type = Unknown-DataId' (will be ignored)
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 276, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 1839, in parser_3220
    ot_type, ot_id, ot_value, ot_schema = decode_frame(payload[2:10])
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/opentherm.py", line 975, in decode_frame
    raise KeyError(
KeyError: 'Unknown data-id: 0x8C (140), msg-type = Unknown-DataId'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/message.py", line 454, in is_valid
    self._payload = payload_parser(self.raw_payload, self)
  File "/usr/local/lib/python3.9/site-packages/ramses_rf/parsers.py", line 278, in wrapper
    raise CorruptPayloadError(f"OpenTherm: {err}")
ramses_rf.exceptions.CorruptPayloadError: Corrupt payload: OpenTherm: 'Unknown data-id: 0x8C (140), msg-type = Unknown-DataId' (will be ignored)

The error messages are a bit alarming - it is on my to do list to address this, so they are more appropriate for the end-user.

Nonetheless, ramses_rf treats such things very harshly (and will always do so), for several reasons, including:

  • the protocol is largely undocumented
  • packet corrupt rates are very high

In short, ramses_rf (the library used by evohome_cc) has no idea what to do with OpenTherm DataID 140 (0x8C) (i.e. no idea how to decode it).

This would normally suggest a corrupt packet as ramses_rf ‘knows’ all the known DataIDs, but the fact that these are coming from a controller, and I presume you’re getting them every 2 minutes, tells me that they are a thing.

If you want to pursue it, the first step would be useful to get a 24h packet log from you, for me to puzzle over for hints as to what this DataID is for.

No problem. I’ll send over tomorrow evening.

Dan

I have a pair of packet logs from you from Feb/Mar this year (all my test/dev is against such data) - I see you have the same controller, and the same OTB (an R8820).

I see the controller wasn’t sending these 0x8C (140) RQs back then!

In addition, I see even the OTB says it doesn’t know the DataID, it replies: Unknown-DataId.

2021-08-14T20:15:48.984970 045 RQ --- 01:210309 10:022270 --:------ 3220 005 00808C0000 << AssertionError(OpenTherm: Invalid data-id: 0x8C (140), msg-type = Read-Data)
2021-08-14T20:15:49.013961 047 RP --- 10:022270 01:210309 --:------ 3220 005 00708C0000 << Coding error: KeyError('Unknown data-id: 0x8C (140), msg-type = Unknown-DataId')

2021-08-14T20:20:50.830645 044 RQ --- 01:210309 10:022270 --:------ 3220 005 00808C0000 << AssertionError(OpenTherm: Invalid data-id: 0x8C (140), msg-type = Read-Data)
2021-08-14T20:20:50.859644 047 RP --- 10:022270 01:210309 --:------ 3220 005 00B08C0000 << AssertionError(OpenTherm: Invalid data-id: 0x8C (140), msg-type = -reserved-)

2021-08-14T20:25:52.497375 044 RQ --- 01:210309 10:022270 --:------ 3220 005 00808C0000 << AssertionError(OpenTherm: Invalid data-id: 0x8C (140), msg-type = Read-Data)
2021-08-14T20:25:52.525372 047 RP --- 10:022270 01:210309 --:------ 3220 005 00708C0000 << Coding error: KeyError('Unknown data-id: 0x8C (140), msg-type = Unknown-DataId')

BTW, I’m thinking the packet at 20:20:50.859644 is corrupt.

So what are doing in Summer that you weren’t doing in Winter?

Or does this behaviour persist after rebooting the controller?

It’s intriguing - I have another OTB (another R8820) doing the periodic msg-type = -reserved-.

I have no other systems doing it, and no systems at all send RQ/8Cs.

Unhelpfully a few changes:
* All but one zone is using faked sensors.
* I’ve reinstated a BDR91 to open and close the heating valve circuits.
* I no longer have an OpenTherm Gateway in between the OTB and the boiler.
* The controller is set to Custom Mode, aka my Summer settings.

For me the removal of the OpenTherm Gateway seems like the most likely cause.

I’ll reboot the controller and see what happens.

Controller reboot appears to have cleared the fault. I’ll continue to monitor throughout today.

Hi @zxdavb - any news on the climate zone unavailable status issue at all? Do you need any more debugging information at all that I could provide? Just as a recap, When I set the preset mode of the controller to Eco or Auto, it works initially, but after a delay (15 minutes at least, but not much longer) all zones drop out as unavailable, except the controller status itself. If I set the controller to Off, all zones set themselves to Off and remain available. So it appears to only happen if it’s not off. I use an HGI80. I think you said previously that you had tweaked the timeouts which had caused it?

Hi @zxdavb,

There is a continuous flow of errors (EXPIRED/retries) in my homeassistant.log file. Can you help to investigate the cause?

Part of the log:

2021-08-19 11:52:15 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data( I|63:262142|7FFF): boff=0, want= I|63:262142|7FFF, tout=2021-08-19 11:52:15.436478: RE-SENT (1/24)
2021-08-19 11:52:15 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data( I|63:262142|7FFF): boff=0, want= I|63:262142|7FFF, tout=2021-08-19 11:52:15.486680: RE-SENT (2/24)
2021-08-19 11:52:15 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data( I|63:262142|7FFF): boff=0, want= I|63:262142|7FFF, tout=2021-08-19 11:52:15.753402: RE-SENT (3/24)
2021-08-19 11:52:15 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data( I|63:262142|7FFF): boff=0, want= I|63:262142|7FFF, tout=2021-08-19 11:52:15.850331: RE-SENT (4/24)
2021-08-19 11:52:20 INFO (MainThread) [ramses_rf.message] Message( I|04:162895|30C9), received at 2021-08-19 10:49:19.908211: msg has expired (1:03:00.303175, 1:00:00)
2021-08-19 11:52:20 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3EF1): boff=1, want=RQ|10:128742|3EF1, tout=2021-08-19 11:52:20.596250: RE-SENT (1/2)
2021-08-19 11:52:20 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3EF1): boff=2, want=RQ|10:128742|3EF1, tout=2021-08-19 11:52:20.832099: RE-SENT (2/2)
2021-08-19 11:52:20 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3EF1): boff=2, want=RP|10:128742|3EF1, tout=2021-08-19 11:52:20.864371: EXPIRED (2/2)
2021-08-19 11:52:22 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|00): boff=0, want=RP|10:128742|3220|00, tout=2021-08-19 11:52:21.981843: EXPIRED (0/0)
2021-08-19 11:52:22 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|11): boff=0, want=RQ|10:128742|3220|11, tout=2021-08-19 11:52:22.902417: EXPIRED (0/0)
2021-08-19 11:52:23 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|12): boff=0, want=RQ|10:128742|3220|12, tout=2021-08-19 11:52:23.001235: EXPIRED (0/0)
2021-08-19 11:52:23 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|19): boff=0, want=RQ|10:128742|3220|19, tout=2021-08-19 11:52:23.112878: EXPIRED (0/0)
2021-08-19 11:52:24 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|1A): boff=0, want=RP|10:128742|3220|1A, tout=2021-08-19 11:52:24.216092: EXPIRED (0/0)
2021-08-19 11:52:25 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|1B): boff=0, want=RP|10:128742|3220|1B, tout=2021-08-19 11:52:25.270925: EXPIRED (0/0)
...
...
2021-08-19 11:53:24 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|19): boff=0, want=RP|10:128742|3220|19, tout=2021-08-19 11:53:24.444862: EXPIRED (0/0)
2021-08-19 11:53:25 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|1A): boff=0, want=RP|10:128742|3220|1A, tout=2021-08-19 11:53:25.481068: EXPIRED (0/0)
2021-08-19 11:53:26 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|1B): boff=0, want=RP|10:128742|3220|1B, tout=2021-08-19 11:53:26.518190: EXPIRED (0/0)
2021-08-19 11:53:27 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|1C): boff=0, want=RP|10:128742|3220|1C, tout=2021-08-19 11:53:27.560501: EXPIRED (0/0)
2021-08-19 11:53:52 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|38): boff=0, want=RQ|10:128742|3220|38, tout=2021-08-19 11:53:52.162877: RE-SENT (1/1)
2021-08-19 11:53:52 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|39): boff=0, want=RQ|10:128742|3220|39, tout=2021-08-19 11:53:52.811193: RE-SENT (1/1)
2021-08-19 11:53:53 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|39): boff=0, want=RP|10:128742|3220|39, tout=2021-08-19 11:53:53.795277: EXPIRED (1/1)
2021-08-19 11:53:54 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|05): boff=0, want=RQ|10:128742|3220|05, tout=2021-08-19 11:53:54.906859: RE-SENT (1/1)
2021-08-19 11:53:55 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|05): boff=0, want=RP|10:128742|3220|05, tout=2021-08-19 11:53:55.890112: EXPIRED (1/1)
2021-08-19 11:53:56 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|71): boff=0, want=RQ|10:128742|3220|71, tout=2021-08-19 11:53:56.997606: RE-SENT (1/1)
2021-08-19 11:53:57 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|71): boff=0, want=RP|10:128742|3220|71, tout=2021-08-19 11:53:57.981733: EXPIRED (1/1)
2021-08-19 11:53:59 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|79): boff=0, want=RQ|10:128742|3220|79, tout=2021-08-19 11:53:59.740405: RE-SENT (1/1)
2021-08-19 11:54:00 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|79): boff=0, want=RP|10:128742|3220|79, tout=2021-08-19 11:54:00.729917: EXPIRED (1/1)
2021-08-19 11:54:01 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|7B): boff=0, want=RQ|10:128742|3220|7B, tout=2021-08-19 11:54:01.817530: RE-SENT (1/1)
2021-08-19 11:54:02 WARNING (MainThread) [ramses_rf.transport] PktProtocolQos.send_data(RQ|10:128742|3220|7B): boff=0, want=RP|10:128742|3220|7B, tout=2021-08-19 11:54:02.800518: EXPIRED (1/1)

I am using an nanoCUL with the latest evofw3.

I had a allow_list configured which I thought could be part of the cause, but moving back to schema-only doesn’t seem to resolve the issue.

If you want, I can share my packet.log and filtered homeassistant.log, and even enable additional debugging if that helps. Do you want me to create a github issue in the evohome_cc for this?