From 4fec7f90321d070b48ac1a5285a70de0cb9ed54d Mon Sep 17 00:00:00 2001 From: septicwolf818 Date: Tue, 11 Aug 2026 22:57:06 +0200 Subject: [PATCH 1/3] Fix zone parsing desync on OpenRGB 1.0rc3+ servers Servers with SDK protocol >= 5 serialize a matrix map block for every zone, not just MATRIX zones. ZoneData.unpack only consumed it when zone_type == MATRIX, leaving the parser 8 bytes behind on LINEAR/SINGLE zones and producing garbage values (e.g. "256 is not a valid ZoneType") during device discovery. Consume the matrix block whenever matrix_zone_size > 0, which is backward compatible since the size is 0 when no map exists. Also widen ZoneType to the current server values (LINEAR_LOOP, MATRIX_LOOP_X, MATRIX_LOOP_Y, SEGMENTED) and add a _missing_ fallback so future types don't crash. --- openrgb/utils.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/openrgb/utils.py b/openrgb/utils.py index 7403495..1683d1d 100644 --- a/openrgb/utils.py +++ b/openrgb/utils.py @@ -66,6 +66,17 @@ class ZoneType(IntEnum): SINGLE = 0 LINEAR = 1 MATRIX = 2 + LINEAR_LOOP = 3 + MATRIX_LOOP_X = 4 + MATRIX_LOOP_Y = 5 + SEGMENTED = 6 + + @classmethod + def _missing_(cls, value): + member = int.__new__(cls, value) + member._name_ = f"UNKNOWN_{value}" + member._value_ = value + return member class PacketType(IntEnum): @@ -468,7 +479,7 @@ def unpack(cls, data: Iterator[int], version: int, *args) -> ZoneData: leds_max = parse_var('I', data) num_leds = parse_var('I', data) matrix_zone_size = parse_var('H', data) - if zone_type == ZoneType.MATRIX: + if matrix_zone_size > 0: height = parse_var('I', data) width = parse_var('I', data) matrix: list[list[Optional[int]]] = [[] for x in range(height)] From 73bf11aa971db3cfbff8687023d6b54f208b2e74 Mon Sep 17 00:00:00 2001 From: septicwolf818 Date: Tue, 18 Aug 2026 10:57:50 +0200 Subject: [PATCH 2/3] Align matrix map serialization with the OpenRGB protocol ZoneData.pack wrote the element count in the matrix size field instead of the byte length the protocol defines (8 + w*h*4), and struct.pack used native alignment which inserted 2 phantom bytes after the size field, shifting height/width and desyncing any packed matrix zone. Also crashed on None values (0xFFFFFFFF map holes converted by unpack) and on zones with no map (mat_height/mat_width None), breaking save_profile(local=True). Use a packed layout with the correct byte length, map None back to 0xFFFFFFFF, and skip empty maps like the server does. --- openrgb/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openrgb/utils.py b/openrgb/utils.py index 1683d1d..ee75dad 100644 --- a/openrgb/utils.py +++ b/openrgb/utils.py @@ -449,12 +449,12 @@ def pack(self, version: int) -> bytes: self.num_leds ) ) - if self.mat_height > 0 and self.mat_width > 0: # type: ignore - flat = [i for li in self.matrix_map for i in li] # type: ignore + if self.mat_height and self.mat_width: # type: ignore + flat = [i if i is not None else 0xFFFFFFFF for li in self.matrix_map for i in li] # type: ignore assert len(flat) == (self.mat_width * self.mat_height) # type: ignore data += struct.pack( - f"HII{len(flat)}I", - len(flat), + f"=HII{len(flat)}I", + 8 + len(flat) * 4, self.mat_height, self.mat_width, *flat From 4212b2f47d14821a599040f50886a1772f6f83bf Mon Sep 17 00:00:00 2001 From: septicwolf818 Date: Tue, 18 Aug 2026 22:12:24 +0200 Subject: [PATCH 3/3] Keep zone parsing aligned with OpenRGB --- openrgb/utils.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/openrgb/utils.py b/openrgb/utils.py index ee75dad..0b01777 100644 --- a/openrgb/utils.py +++ b/openrgb/utils.py @@ -71,13 +71,6 @@ class ZoneType(IntEnum): MATRIX_LOOP_Y = 5 SEGMENTED = 6 - @classmethod - def _missing_(cls, value): - member = int.__new__(cls, value) - member._name_ = f"UNKNOWN_{value}" - member._value_ = value - return member - class PacketType(IntEnum): REQUEST_CONTROLLER_COUNT = 0 @@ -449,12 +442,12 @@ def pack(self, version: int) -> bytes: self.num_leds ) ) - if self.mat_height and self.mat_width: # type: ignore - flat = [i if i is not None else 0xFFFFFFFF for li in self.matrix_map for i in li] # type: ignore + if self.mat_height > 0 and self.mat_width > 0: # type: ignore + flat = [i for li in self.matrix_map for i in li] # type: ignore assert len(flat) == (self.mat_width * self.mat_height) # type: ignore data += struct.pack( - f"=HII{len(flat)}I", - 8 + len(flat) * 4, + f"HII{len(flat)}I", + len(flat), self.mat_height, self.mat_width, *flat