Skip to content
David Bonnes edited this page Aug 17, 2021 · 7 revisions

The basic bind appears to be a 3-way handshake (offer, accept, confirm), followed by series of packets to scynchronise state:

17:23:09.641 || DHW:045960 |            |  I | rf_bind          | 00126... || [['00', '1260', '07:045960'], ['00', '1FC9', '07:045960']]
17:23:09.656 || CTL:145038 | DHW:045960 |  W | rf_bind          | 0010A... || [['00', '10A0', '01:145038']]
17:23:09.680 || DHW:045960 | CTL:145038 |  I | rf_bind          | 00126... || [['00', '1260', '07:045960']]
17:23:10.750 || CTL:145038 |            |  I | system_zones     | 000D0... || {'zone_mask': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'zone_type': 'hotwater_sensor'}

17:23:12.590 || DHW:045960 | CTL:145038 | RQ | dhw_params       | 00137... || {'setpoint': 49.8, 'overrun': 0, 'differential': 9.96}
17:23:12.606 || CTL:145038 | DHW:045960 | RP | dhw_params       | 00138... || {'dhw_idx': '00', 'setpoint': 50.0, 'overrun': 0, 'differential': 10.0}

For orientation, the accept packet is the W. The system_zones packet is sent only if the system schema is changed (e.g. a new zone, but not an additional TRV to an existing zone).

See: https://github.com/zxdavb/ramses_protocol/wiki/1FC9:-Binding-BDR91As for more detailed examples.

Here is another example:

17:20:53.540 || CTL:145038 |            |  I | rf_bind          | FC000... || [['FC', '0008', '01:145038'], ['FC', '3150', '01:145038'], ['FB', '3150', '01:145038'], ['FC', '1FC9', '01:145038']]
17:20:54.933 || BDR:106039 | CTL:145038 |  W | rf_bind          | 003EF... || [['00', '3EF0', '13:106039'], ['00', '3B00', '13:106039']]
17:20:54.947 || CTL:145038 | BDR:106039 |  I | rf_bind          | 00FFF... || [['00', 'FFFF', '01:145038']]
17:20:55.650 || CTL:145038 |            |  I | system_zones     | 000F0... || {'zone_mask': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'zone_type': 'heating_control'}

Firstly, a device broadcasts an array of domain/code pairs. Here is a DHW sensor:

17:23:09.641 || DHW:045960 |            |  I | rf_bind          | 00126... || [['00', '1260', '07:045960'], ['00', '1FC9', '07:045960']]

This is what the device has to offer.

Note that the controller may broadcast the offer, depending:

17:20:53.540 || CTL:145038 |            |  I | rf_bind          | FC000... || [['FC', '0008', '01:145038'], ['FC', '3150', '01:145038'], ['FB', '3150', '01:145038'], ['FC', '1FC9', '01:145038']]

Then the offer is accepted with a complementary offer, here are some examples:

17:15:12.908 || BDR:163733 | CTL:145038 |  W | rf_bind          | 003EF... || [['00', '3EF0', '13:163733']]
17:20:54.933 || BDR:106039 | CTL:145038 |  W | rf_bind          | 003EF... || [['00', '3EF0', '13:106039'], ['00', '3B00', '13:106039']]
17:23:09.656 || CTL:145038 | DHW:045960 |  W | rf_bind          | 0010A... || [['00', '10A0', '01:145038']]
12:24:11.120 || CTL:145038 | STA:064023 |  W | rf_bind          | 07230... || [['07', '2309', '01:145038']]
13:30:25.766 || CTL:145038 | THm:010740 |  W | rf_bind          | 03230... || [['03', '2309', '01:145038']]

Request or return device binding information.

045  I --- 10:067219 63:262142 --:------ 1FC9 006 003EF0290693

045  I --- 01:078710 --:------ 01:078710 1FC9 024 FC0008053376FC3150053376FB3150053376FC1FC9053376

045  W --- 10:067219 01:078710 --:------ 1FC9 006 003EF0290693

045  I --- 01:078710 10:067219 --:------ 1FC9 006 00FFFF053376
Payload decode: To do

Sample Code

Using Python, the payload can be decoded as:

def parser_1fc9(payload, msg) -> Optional[dict]:  # bind_device
    def _parser(seqx) -> dict:
        if seqx[:2] not in ["FB", "FC"]:
            assert int(seqx[:2], 16) <= 11
        return {
            "zone_idx" if int(payload[:2], 16) <= 11 else "domain": _id(payload[:2]),
            "command": COMMAND_MAP.get(seqx[2:6], f"unknown_{seqx[2:6]}"),
            "device_id": dev_hex_to_id(seqx[6:]),
        }
    assert len(payload) / 2 % 6 == 0
    return [_parser(payload[i : i + 12]) for i in range(0, len(payload), 12)]
Clone this wiki locally