I wrote some python code that could be a starting point for others to help decode the data.
import socket
import struct
# Contains 1 message found over serial
class Packet:
def __init__(self):
self.header = []
self.length = 0
self.payload = []
self.footer = []
def __str__(self):
return str(self.header) + "-" + str(self.length) + "-" + str(self.payload) + "-" + str(self.footer)
def toBytes(self):
return self.header + [self.length] + self.payload + self.footer
# State machine classes
class StaticMatchState:
def __init__(self, packet, pattern):
self.packet = packet
self.pattern = pattern
self.i = 0
def consume(self, b):
if b == self.pattern[self.i]:
#print(f"{b} matches the next part of the pattern")
self.packet.header.append(b)
self.i += 1
if self.i == len(self.pattern):
#print(f"Found the whole pattern: {self.pattern}")
return LengthState(self.packet)
return self
else:
#print(f"{b} doesn't match {self.pattern[i]}, reseting state")
return StaticMatchState(Packet(), self.pattern)
class LengthState:
def __init__(self, packet):
self.packet = packet
def consume(self, b):
self.packet.length = b
return PayloadState(self.packet, b)
class PayloadState:
def __init__(self, packet, length):
self.packet = packet
self.length = length
self.consumed = 0
def consume(self, b):
self.packet.payload.append(b)
self.consumed += 1
if self.consumed >= self.length:
return FooterState(self.packet, 2)
return self
class FooterState:
def __init__(self, packet, length):
self.packet = packet
self.length = length
self.consumed = 0
def consume(self, b):
self.packet.footer.append(b)
self.consumed += 1
if self.consumed >= self.length:
return self.packet
return self
def startMatchingState():
return StaticMatchState(Packet(), [0x1C, 0x02, 0x01])
# Convert TCP bytes into Packet objects
def getPackets(ip, port):
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket.connect((ip, port))
state = startMatchingState()
while True:
result = clientsocket.recv(16)
for b in result:
state = state.consume(b)
if isinstance(state, Packet):
yield state
state = startMatchingState()
# Code that interprets Packet.payload data into something a human can understand (WIP)
def asHex(b):
return hex(b)
def signedChar(b):
return struct.unpack('>1b',bytes([b]))[0]
flags = {
7: {
"VacationFlag1?": 0b00000001
},
12: {
"VacationFlag2?": 0b00000001
},
13: {
"PureAir": 0b00010000,
"IceMakerOn": 0b01000000
},
14: {
"IceMakerMaxIce": 0b00000001,
"AlarmBeeping": 0b00001000,
"AlarmOn": 0b00000100
},
15: {
"IceMakerNightMode": 0b00000001
}
}
def decodePayload(old, new):
for i in range(17):
label = f"payload[{i}]"
unpackData = asHex
if i == 3:
label = "Fridge Set Point"
unpackData = signedChar
elif i == 5 or i == 6:
label = "Long Vacation timestamp?" # these change when setting long vacation mode, not sure what it is yet
elif i == 8:
label = "Freezer Set Point"
unpackData = signedChar
elif i in flags:
label = f"Flags[{i}]"
if old[i] == new[i]:
print(f"{label}: {unpackData(old[i])}")
else:
print(f"{label}: {unpackData(old[i])} -> {unpackData(new[i])}")
if i in flags:
for flag in sorted(flags[i].keys()):
oldFlag = (old[i] & flags[i][flag]) > 0
newFlag = (new[i] & flags[i][flag]) > 0
if (oldFlag == newFlag):
print(f" {flag}: {oldFlag}")
else:
print(f" {flag}: {oldFlag} -> {newFlag}")
print()
seenPackets = {}
# packet that arrived before this one, used for `uniq` style deduping based on payload bytes
previousPacketPayload = None
# previous status packet that arrived before this status packet, used for `uniq` style deduping of status packets
previousStatusPayload = None
def isStatusPacket(packet):
payload = packet.payload
# works at the moment, not sure if its correct over time or for other refridgerators
return len(payload) == 17 and payload[0] == 0x40 and payload[1] == 0x00 and payload[2] == 0x14
for packet in getPackets('192.168.20.70', 8888):
payload = packet.payload
payloadBytes = " ".join(f"{n:#04x}" for n in payload)
if previousPacketPayload is None or previousPacketPayload != payload:
packetType = "Unknown"
if payloadBytes.startswith("0x40 0x00 0x14"):
packetType = "Status"
elif payloadBytes == "0x40 0x07":
packetType = "Door Open"
print(f"New Payload ({packetType}): {payloadBytes}")
previousPacketPayload = payload
# special decoding for status packets with diffing against previous status
if isStatusPacket(packet):
if previousStatusPayload and previousStatusPayload != payload:
decodePayload(previousStatusPayload, payload)
previousStatusPayload = payload
if str(packet) not in seenPackets:
print(f"Found a new unique packet with payload: {payloadBytes}")
seenPackets[str(packet)] = packet
Here’s an example of the output:
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
Found a new unique packet with payload: 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x40 0x00 0x16
Found a new unique packet with payload: 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
Found a new unique packet with payload: 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Door Open): 0x40 0x07
Found a new unique packet with payload: 0x40 0x07
Found a new unique packet with payload: 0x40 0x07
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x80 0x07
Found a new unique packet with payload: 0x80 0x07
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xfe 0xff 0x55 0x41 0x04 0x54 0x04 0x00 0x50
payload[0]: 0x40
payload[1]: 0x0
payload[2]: 0x14
Fridge Set Point: 38
payload[4]: 0x0
Long Vacation timestamp?: 0x4c
Long Vacation timestamp?: 0x6e
Flags[7]: 0x0
Freezer Set Point: -1 -> -2
payload[9]: 0xff
payload[10]: 0x55
payload[11]: 0x41
Flags[12]: 0x0 -> 0x4
VacationFlag2?: False
Flags[13]: 0x54
Flags[14]: 0x4
Flags[15]: 0x0
payload[16]: 0x50
Found a new unique packet with payload: 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xfe 0xff 0x55 0x41 0x04 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x80 0x07
New Payload (Door Open): 0x40 0x07
New Payload (Unknown): 0x80 0x07
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x04 0x54 0x04 0x00 0x50
payload[0]: 0x40
payload[1]: 0x0
payload[2]: 0x14
Fridge Set Point: 38
payload[4]: 0x0
Long Vacation timestamp?: 0x4c
Long Vacation timestamp?: 0x6e
Flags[7]: 0x0
Freezer Set Point: -2 -> -1
payload[9]: 0xff
payload[10]: 0x55
payload[11]: 0x41
Flags[12]: 0x4
Flags[13]: 0x54
Flags[14]: 0x4
Flags[15]: 0x0
payload[16]: 0x50
Found a new unique packet with payload: 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x04 0x54 0x04 0x00 0x50
New Payload (Unknown): 0x80 0x07
New Payload (Unknown): 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x04 0x54 0x04 0x00 0x50
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
payload[0]: 0x40
payload[1]: 0x0
payload[2]: 0x14
Fridge Set Point: 38
payload[4]: 0x0
Long Vacation timestamp?: 0x4c
Long Vacation timestamp?: 0x6e
Flags[7]: 0x0
Freezer Set Point: -1
payload[9]: 0xff
payload[10]: 0x55
payload[11]: 0x41
Flags[12]: 0x4 -> 0x0
VacationFlag2?: False
Flags[13]: 0x54
Flags[14]: 0x4
Flags[15]: 0x0
payload[16]: 0x50
New Payload (Unknown): 0x40 0x00 0x16
New Payload (Unknown): 0x40 0x14 0x01
New Payload (Status): 0x40 0x00 0x14 0x26 0x00 0x4c 0x6e 0x00 0xff 0xff 0x55 0x41 0x00 0x54 0x04 0x00 0x50
I’ve filled in some of the easy parts (things I can change in the app or touching the fridge control panel), but there’s more data to “decode.”
I am using an ESP32 running tasmota along with this board Amazon.com: Teyleten Robot TTL to RS485 Module 485 to Serial UART Level Mutual Conversion Hardware Automatic Flow Control Module TTL Turn to RS485 Module 3.3V-5.5V (5pcs) : Electronics
Tasmota has a serial to TCP bridge functionality, which I enabled using these commands in the Tasmota console:
TCPBaudRate 38400
TCPStart 8888