From f54ea71f68bd6491ad6ff2f582ceebe927a5ca73 Mon Sep 17 00:00:00 2001 From: Samson Brock Date: Sat, 29 Aug 2026 12:40:03 +0000 Subject: [PATCH] Fix IndexError crash in TCP packet parser for packet types 115 and 131 Several device-index lookups (self.home_devices[home_id][idx]) in the packet_type == 115 and packet_type == 131 handlers were unguarded, unlike the equivalent lookup in the packet_type == 67 handler, which already checks the index against len(self.home_devices[home_id]) before using it. When Cync's cloud sends a packet whose device index isn't present in the locally cached home_devices list, this throws an uncaught IndexError: list index out of range, dropping the packet entirely (caught by the outer try/except and logged as 'Error parsing an incoming Cync TCP packet', with no further handling). Added the same bounds check already used in the packet_type == 67 handler to the 5 remaining unguarded lookups, so a packet referencing an out-of-range index is safely skipped instead of crashing the parse loop. --- custom_components/cync_lights/cync_hub.py | 81 ++++++++++++----------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/custom_components/cync_lights/cync_hub.py b/custom_components/cync_lights/cync_hub.py index fdc677a..23ebc3e 100644 --- a/custom_components/cync_lights/cync_hub.py +++ b/custom_components/cync_lights/cync_hub.py @@ -141,20 +141,22 @@ async def _read_tcp_messages(self): if packet_length >= 33 and int(packet[13]) == 219: #parse state and brightness change packet - deviceID = self.home_devices[home_id][int(packet[21])] - state = int(packet[27]) > 0 - brightness = int(packet[28]) if state else 0 - if deviceID in self.cync_switches: - self.cync_switches[deviceID].update_switch(state,brightness,self.cync_switches[deviceID].color_temp,self.cync_switches[deviceID].rgb) + if int(packet[21]) < len(self.home_devices[home_id]): + deviceID = self.home_devices[home_id][int(packet[21])] + state = int(packet[27]) > 0 + brightness = int(packet[28]) if state else 0 + if deviceID in self.cync_switches: + self.cync_switches[deviceID].update_switch(state,brightness,self.cync_switches[deviceID].color_temp,self.cync_switches[deviceID].rgb) elif packet_length >= 25 and int(packet[13]) == 84: #parse motion and ambient light sensor packet - deviceID = self.home_devices[home_id][int(packet[16])] - motion = int(packet[22]) > 0 - ambient_light = int(packet[24]) > 0 - if deviceID in self.cync_motion_sensors: - self.cync_motion_sensors[deviceID].update_motion_sensor(motion) - if deviceID in self.cync_ambient_light_sensors: - self.cync_ambient_light_sensors[deviceID].update_ambient_light_sensor(ambient_light) + if int(packet[16]) < len(self.home_devices[home_id]): + deviceID = self.home_devices[home_id][int(packet[16])] + motion = int(packet[22]) > 0 + ambient_light = int(packet[24]) > 0 + if deviceID in self.cync_motion_sensors: + self.cync_motion_sensors[deviceID].update_motion_sensor(motion) + if deviceID in self.cync_ambient_light_sensors: + self.cync_ambient_light_sensors[deviceID].update_ambient_light_sensor(ambient_light) elif packet_length > 51 and int(packet[13]) == 82: #parse initial state packet switch_id = str(struct.unpack(">I", packet[0:4])[0]) @@ -162,40 +164,43 @@ async def _read_tcp_messages(self): self._add_connected_devices(switch_id, home_id) packet = packet[22:] while len(packet) > 24: - deviceID = self.home_devices[home_id][int(packet[0])] - if deviceID in self.cync_switches: - if self.cync_switches[deviceID].elements > 1: - for i in range(self.cync_switches[deviceID].elements): - device_id = self.home_devices[home_id][(i+1)*256 + int(packet[0])] - state = int((int(packet[12]) >> i) & int(packet[8])) > 0 - brightness = 100 if state else 0 - self.cync_switches[device_id].update_switch(state,brightness,self.cync_switches[device_id].color_temp,self.cync_switches[device_id].rgb) - else: - state = int(packet[8]) > 0 - brightness = int(packet[12]) if state else 0 - color_temp = int(packet[16]) - rgb = {'r':int(packet[20]),'g':int(packet[21]),'b':int(packet[22]),'active':int(packet[16])==254} - self.cync_switches[deviceID].update_switch(state,brightness,color_temp,rgb) + if int(packet[0]) < len(self.home_devices[home_id]): + deviceID = self.home_devices[home_id][int(packet[0])] + if deviceID in self.cync_switches: + if self.cync_switches[deviceID].elements > 1: + for i in range(self.cync_switches[deviceID].elements): + device_id = self.home_devices[home_id][(i+1)*256 + int(packet[0])] + state = int((int(packet[12]) >> i) & int(packet[8])) > 0 + brightness = 100 if state else 0 + self.cync_switches[device_id].update_switch(state,brightness,self.cync_switches[device_id].color_temp,self.cync_switches[device_id].rgb) + else: + state = int(packet[8]) > 0 + brightness = int(packet[12]) if state else 0 + color_temp = int(packet[16]) + rgb = {'r':int(packet[20]),'g':int(packet[21]),'b':int(packet[22]),'active':int(packet[16])==254} + self.cync_switches[deviceID].update_switch(state,brightness,color_temp,rgb) packet = packet[24:] elif packet_type == 131: switch_id = str(struct.unpack(">I", packet[0:4])[0]) home_id = self.switchID_to_homeID[switch_id] if packet_length >= 33 and int(packet[13]) == 219: #parse state and brightness change packet - deviceID = self.home_devices[home_id][int(packet[21])] - state = int(packet[27]) > 0 - brightness = int(packet[28]) if state else 0 - if deviceID in self.cync_switches: - self.cync_switches[deviceID].update_switch(state,brightness,self.cync_switches[deviceID].color_temp,self.cync_switches[deviceID].rgb) + if int(packet[21]) < len(self.home_devices[home_id]): + deviceID = self.home_devices[home_id][int(packet[21])] + state = int(packet[27]) > 0 + brightness = int(packet[28]) if state else 0 + if deviceID in self.cync_switches: + self.cync_switches[deviceID].update_switch(state,brightness,self.cync_switches[deviceID].color_temp,self.cync_switches[deviceID].rgb) elif packet_length >= 25 and int(packet[13]) == 84: #parse motion and ambient light sensor packet - deviceID = self.home_devices[home_id][int(packet[16])] - motion = int(packet[22]) > 0 - ambient_light = int(packet[24]) > 0 - if deviceID in self.cync_motion_sensors: - self.cync_motion_sensors[deviceID].update_motion_sensor(motion) - if deviceID in self.cync_ambient_light_sensors: - self.cync_ambient_light_sensors[deviceID].update_ambient_light_sensor(ambient_light) + if int(packet[16]) < len(self.home_devices[home_id]): + deviceID = self.home_devices[home_id][int(packet[16])] + motion = int(packet[22]) > 0 + ambient_light = int(packet[24]) > 0 + if deviceID in self.cync_motion_sensors: + self.cync_motion_sensors[deviceID].update_motion_sensor(motion) + if deviceID in self.cync_ambient_light_sensors: + self.cync_ambient_light_sensors[deviceID].update_ambient_light_sensor(ambient_light) elif packet_type == 67 and packet_length >= 26 and int(packet[4]) == 1 and int(packet[5]) == 1 and int(packet[6]) == 6: #parse state packet switch_id = str(struct.unpack(">I", packet[0:4])[0])