Skip to content
This repository has been archived by the owner on Nov 6, 2024. It is now read-only.

Latest commit

 

History

History
281 lines (234 loc) · 13.6 KB

PROTOCOL.md

File metadata and controls

281 lines (234 loc) · 13.6 KB

ComfoControl Protocol

This document tries to explain the ComfoControl Protocol used by Zehnder ventilation units. You need a ComfoConnect LAN C device to interface with the unit.

Warning: This documentation is incomplete. If you have more information, don't hesitate to contribute by opening an issue or making a PR.

General packet structure

The messages are send in a TCP-connection to port 56747, and are prepended with a header that resembles the "Length-prefix protocol buffers" format.

This file zehnder.proto contains a Protocol Buffers definition of the protocol.

Manually decoding a packet

The protoc utility can be used to manually decode a message based on the hex representation of the payload. You need to strip the headers and extract the cmd and msg before passing it to protoc. See the sections below to find out what to strip.

Example:

0000004 ea886190220044d68a07d85a2e3866fce 0000000000251010800170b3d54264b4 0004 08022002 0a10a886190220044d68a07d85a2e3866fce10001a126950686f6e652076616e2044657374696e79
length_ src______________________________ dst_____________________________ cmd# cmd_____ msg_____________________________________________________________________________

Command:

$ echo "08022002" | xxd -r -p | protoc --decode=GatewayOperation zehnder.proto
type: RegisterAppRequestType
reference: 2

Message:

$ echo "0a10a886190220044d68a07d85a2e3866fce10001a126950686f6e652076616e2044657374696e79" | xxd -r -p | protoc --decode=RegisterAppRequest zehnder.proto
uuid: "\250\206\031\002 \004Mh\240}\205\242\343\206o\316"
pin: 0
devicename: "iPhone van Destiny"

Device discovery (DiscoveryOperation)

The bridge can be discovered by sending an UDP packet containing 0x0a00 to your network's broadcast address on port 56747. It will respond with the following connection information, also in a UDP packet. The last 6 bytes of the identifier seems to be the bridge's MAC address. This packet contains no header and can be fully passed to Protocol Buffers.

Raw response data:

12230a0d3139322e3136382e312e32313312100000000000251010800170b3d54264b41801
searchGatewayResponse {
  ipaddress: "192.168.1.213"
  uuid: "\000\000\000\000\000%\020\020\200\001p\263\325Bd\264"
  version: 1
}

You now have the bridge's ipaddress and uuid. You need these for further communication.

Bridge communication (GatewayOperation)

Further communication with the bridge happens by opening a TCP connection to the bridge on port 56747. Every message will start with the length of the message, excluding this length-field itself.

Note that only op and msg is valid Protocol Buffers data. The other fields should not be parsed.

Header

The header then consist of a src, dst and op_length, followed by the op and the msg. The src seems to be generated by the client, the dst has to be obtained from the discovery packet (uuid). The op_length fields indicates the length of the op field, the rest of the data contains the msg.

Field Data Remark
length (32 bit) 0x0000004f Length of the whole message excluding this field
src (12 bytes) 0xaf154804169043898d2da77148f886be
dst (12 bytes) 0x0000000000251010800170b3d54264b4
op_length (16 bit 0x0004 Length of the op message
op (variable length) 0x08342002 Message with type GatewayOperation
msg (variable length) ... Message with type that is stated in op.type

Commands

A message consists of a command block (GatewayOperation) and a message block (variable type). The command block contains a type and reference field. The type field indicates the type of the message block. The reference field will contain a incremental number that can be used to link a request with a reply.

This is a list of the commands.

Request Confirm Notification / Response Description
NoOperation
SetAddressRequestType SetAddressConfirmType
RegisterAppRequestType RegisterAppConfirmType Adds a device in the registration list
StartSessionRequestType StartSessionConfirmType Start as session with the bridge
CloseSessionRequestType CloseSessionConfirmType Terminate your session
ListRegisteredAppsRequestType ListRegisteredAppsConfirmType Returns a list of registered apps on the bridge
DeregisterAppRequestType DeregisterAppConfirmType Remove a UUID from the registration list
ChangePinRequestType ChangePinConfirmType Change the PIN code
GetRemoteAccessIdRequestType GetRemoteAccessIdConfirmType
SetRemoteAccessIdRequestType SetRemoteAccessIdConfirmType
GetSupportIdRequestType GetSupportIdConfirmType
GetWebIdRequestType GetWebIdConfirmType
SetWebIdRequestType SetWebIdConfirmType
SetPushIdRequestType SetPushIdConfirmType
DebugRequestType DebugConfirmType
UpgradeRequestType UpgradeConfirmType
SetDeviceSettingsRequestType SetDeviceSettingsConfirmType
VersionRequestType VersionConfirmType
GatewayNotificationType
KeepAliveType You should send these to keep the connection open.
FactoryResetType
CnTimeRequestType CnTimeConfirmType Returns the seconds since 2000-01-01 00:00:00.
CnNodeRequestType CnNodeNotificationType
CnRmiRequestType CnRmiResponseType
CnRmiAsyncRequestType CnRmiAsyncConfirmType CnRmiAsyncResponseType
CnRpdoRequestType CnRpdoConfirmType CnRpdoNotificationType
CnAlarmNotificationType
CnFupReadRegisterRequestType CnFupReadRegisterConfirmType
CnFupProgramBeginRequestType CnFupProgramBeginConfirmType
CnFupProgramRequestType CnFupProgramConfirmType
CnFupProgramEndRequestType CnFupProgramEndConfirmType
CnFupReadRequestType CnFupReadConfirmType
CnFupResetRequestType CnFupResetConfirmType

RegisterApp (RegisterAppRequestType and RegisterAppConfirmType)

Before you can login, you need to register your device, by sending a type: RegisterAppRequestType.

type: RegisterAppRequestType
reference: 15

uuid: "\251\226\031\002 \004Mh\240}\205\242\343\206o\312"
pin: 0
devicename: "Computer"

The bridge will respond with a type: RegisterAppConfirmType. In case of success, it will respond like this:

type: RegisterAppConfirmType
reference: 15

In case of a failure (invalid PIN), it will respond with a result: NOT_ALLOWED.

type: RegisterAppConfirmType
result: NOT_ALLOWED
reference: 15

StartSession (StartSessionRequestType and StartSessionConfirmType)

The client logs in by sending a type: StartSessionRequestType. Only one client can be logged in at the same time.

type: StartSessionRequestType
reference: 16

In case of a success, it will respond with a type: StartSessionRequestType with result: OK.

type: StartSessionConfirmType
result: OK
reference: 16

If another client is already logged in, the bridge will respond with a result of OTHER_SESSION. The name of the other device will be in devicename.

type: StartSessionConfirmType
result: OTHER_SESSION
reference: 16

devicename: "Google Nexus 5X"

You can force the takeover of this session by specifying a takeover: 1.

type: StartSessionConfirmType
result: OK
reference: 17

takeover: 1

Next, we see a few notifications. They seem to be messages to let the client know what nodes are available. We only send messages to nodeId: 1.

type: CnNodeNotificationType

nodeId: 1
productId: 1
zoneId: 1
mode: NODE_NORMAL
type: CnNodeNotificationType

nodeId: 48
productId: 5
zoneId: 255
mode: NODE_NORMAL

These are the known productIds.

productId type description
1 ComfoAirQ The ComfoAirQ ventilation unit.
2 ComfoSense ComfoSense C
3 ComfoSwitch ComfoSwitch C
4 OptionBox
5 ZehnderGateway ComfoConnect LAN C
6 ComfoCool ComfoCool Q600
7 KNXGateway ComfoConnect KNX C
8 Service Tool
9 PT Tool Production test tool
10 DVT Tool Design verification test tool

CloseSession (CloseSessionRequestType)

The client logs out by sending a type: CloseSessionRequestType. The bridge doesn't seem to send a response on this.

type: CloseSessionRequestType

When the session is closed by the bridge (because another client connects with takeover: 1), the bridge will also send a type: CloseSessionRequestType message to the client.

CnRpdoRequest (CnRpdoRequestType, CnRpdoConfirmType and CnRpdoNotificationType)

To receive status updates, you need to register to a pdid by sending a type: CnRpdoRequestType. You also need to specify a pdid, zone, type and timeout. The zone always seems to be 1. The type seems to depend on the pdid.

type: CnRpdoRequestType
reference: 104

pdid: 176
zone: 1
type: 1
timeout: 4294967295

The bridge will reply with a type: CnRpdoConfirmType.

type: CnRpdoConfirmType
result: OK
reference: 104

Next, when an update is available, the bridge will send a type: CnRpdoNotificationType.

type: CnRpdoNotificationType

pdid: 176
data: "\000"

For known PDOs, check PROTOCOL-PDO.md

CnRmiRequest (CnRmiRequestType and CnRmiResponseType)

You can execute a function on the device by invoking a type: CnRmiRequestType. You need to specify the nodeId and a message. This can make a configuration change, or request data.

type: CnRmiRequestType
reference: 122

nodeId: 1
message: "\001\001\001\020\010"

The bridge will respond with a type: CnRmiResponseType.

type: CnRmiResponseType
reference: 122

message: "ComfoAir Q450 B R RF ST Quality\000"

For an overview about the known RMI Requests, check PROTOCOL-RMI.md