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.
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.
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"
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.
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.
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 |
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 |
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
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 productId
s.
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 |
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.
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
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