From abf8c06fe4c37b10cf73d8eb4dcee85bf368c11c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:52:48 +0000 Subject: [PATCH 1/6] Initial plan From b447f70bf782dfdb34c084cd4062044ccd16ec23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:55:42 +0000 Subject: [PATCH 2/6] Handle modbus exception packets in state reads Agent-Logs-Url: https://github.com/bbr111/python-bydhvs/sessions/89807568-498c-413a-9058-daaf24f1ddc3 Co-authored-by: bbr111 <46243805+bbr111@users.noreply.github.com> --- bydhvs/__init__.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/bydhvs/__init__.py b/bydhvs/__init__.py index 2148a2d..e515725 100644 --- a/bydhvs/__init__.py +++ b/bydhvs/__init__.py @@ -657,10 +657,41 @@ async def _send_and_receive( data = await self._receive_response() if data and self._check_packet(data): return data - _LOGGER.error("Invalid or no data received in %s", state_name) + + if data and self._is_modbus_exception_response(data, request): + _LOGGER.warning( + "Modbus exception response in %s for tower %d " + "(exception=0x%02X, len=%d)", + state_name, + self.current_tower, + data[2], + len(data), + ) + self._state = 0 + return None + + if data: + _LOGGER.error( + "Invalid CRC or malformed packet in %s for tower %d (len=%d)", + state_name, + self.current_tower, + len(data), + ) + else: + _LOGGER.error("Invalid or no data received in %s", state_name) self._state = 0 return None + def _is_modbus_exception_response(self, data: bytes, request: bytes) -> bool: + """Check if response is a valid Modbus exception packet.""" + if len(data) != self.MIN_PACKET_LENGTH or len(request) < 2: + return False + if data[0] != self.MODBUS_ADDRESS: + return False + if data[1] != (request[1] | 0x80): + return False + return CRC16(data) == 0 + async def __aenter__(self) -> 'BYDHVS': """Async context manager entry.""" return self From 3099d5a0a58d35f2ed4ed15b0266dafc19955141 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:56:36 +0000 Subject: [PATCH 3/6] Refine modbus exception packet constant usage Agent-Logs-Url: https://github.com/bbr111/python-bydhvs/sessions/89807568-498c-413a-9058-daaf24f1ddc3 Co-authored-by: bbr111 <46243805+bbr111@users.noreply.github.com> --- bydhvs/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bydhvs/__init__.py b/bydhvs/__init__.py index e515725..144173f 100644 --- a/bydhvs/__init__.py +++ b/bydhvs/__init__.py @@ -121,6 +121,7 @@ class BYDHVS: BUFFER_SIZE = 1024 # Packet structure constants MIN_PACKET_LENGTH = 5 + MODBUS_EXCEPTION_PACKET_LENGTH = 5 PACKET_HEADER_SIZE = 3 PACKET_CRC_SIZE = 2 PACKET5_MIN_LENGTH = 133 @@ -659,12 +660,13 @@ async def _send_and_receive( return data if data and self._is_modbus_exception_response(data, request): + exception_code = data[2] _LOGGER.warning( "Modbus exception response in %s for tower %d " "(exception=0x%02X, len=%d)", state_name, self.current_tower, - data[2], + exception_code, len(data), ) self._state = 0 @@ -684,7 +686,10 @@ async def _send_and_receive( def _is_modbus_exception_response(self, data: bytes, request: bytes) -> bool: """Check if response is a valid Modbus exception packet.""" - if len(data) != self.MIN_PACKET_LENGTH or len(request) < 2: + if ( + len(data) != self.MODBUS_EXCEPTION_PACKET_LENGTH + or len(request) < 2 + ): return False if data[0] != self.MODBUS_ADDRESS: return False From 76db03df1f163e0b775c4ec65872db6ee873fd1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:57:16 +0000 Subject: [PATCH 4/6] Extract modbus exception parsing constants Agent-Logs-Url: https://github.com/bbr111/python-bydhvs/sessions/89807568-498c-413a-9058-daaf24f1ddc3 Co-authored-by: bbr111 <46243805+bbr111@users.noreply.github.com> --- bydhvs/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bydhvs/__init__.py b/bydhvs/__init__.py index 144173f..cfc895b 100644 --- a/bydhvs/__init__.py +++ b/bydhvs/__init__.py @@ -122,10 +122,12 @@ class BYDHVS: # Packet structure constants MIN_PACKET_LENGTH = 5 MODBUS_EXCEPTION_PACKET_LENGTH = 5 + MIN_MODBUS_REQUEST_LENGTH = 2 PACKET_HEADER_SIZE = 3 PACKET_CRC_SIZE = 2 PACKET5_MIN_LENGTH = 133 MODBUS_ADDRESS = 1 + MODBUS_EXCEPTION_FLAG = 0x80 FUNCTION_CODE_READ = 3 FUNCTION_CODE_WRITE = 16 @@ -688,12 +690,12 @@ def _is_modbus_exception_response(self, data: bytes, request: bytes) -> bool: """Check if response is a valid Modbus exception packet.""" if ( len(data) != self.MODBUS_EXCEPTION_PACKET_LENGTH - or len(request) < 2 + or len(request) < self.MIN_MODBUS_REQUEST_LENGTH ): return False if data[0] != self.MODBUS_ADDRESS: return False - if data[1] != (request[1] | 0x80): + if data[1] != (request[1] | self.MODBUS_EXCEPTION_FLAG): return False return CRC16(data) == 0 From f84f557ee24026bf38623f55c405810fea1d9855 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:58:01 +0000 Subject: [PATCH 5/6] Document modbus exception packet shape Agent-Logs-Url: https://github.com/bbr111/python-bydhvs/sessions/89807568-498c-413a-9058-daaf24f1ddc3 Co-authored-by: bbr111 <46243805+bbr111@users.noreply.github.com> --- bydhvs/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bydhvs/__init__.py b/bydhvs/__init__.py index cfc895b..0c2962c 100644 --- a/bydhvs/__init__.py +++ b/bydhvs/__init__.py @@ -122,6 +122,7 @@ class BYDHVS: # Packet structure constants MIN_PACKET_LENGTH = 5 MODBUS_EXCEPTION_PACKET_LENGTH = 5 + # Request needs at least address and function code bytes MIN_MODBUS_REQUEST_LENGTH = 2 PACKET_HEADER_SIZE = 3 PACKET_CRC_SIZE = 2 @@ -687,7 +688,11 @@ async def _send_and_receive( return None def _is_modbus_exception_response(self, data: bytes, request: bytes) -> bool: - """Check if response is a valid Modbus exception packet.""" + """Check if response is a valid Modbus exception packet. + + A valid Modbus exception response is 5 bytes: + address, function|0x80, exception_code, crc_lo, crc_hi. + """ if ( len(data) != self.MODBUS_EXCEPTION_PACKET_LENGTH or len(request) < self.MIN_MODBUS_REQUEST_LENGTH From 852e9e448c7919fa0653ec26c5b092e0d1b1362b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 19:58:41 +0000 Subject: [PATCH 6/6] Guard exception parsing against mismatched request address Agent-Logs-Url: https://github.com/bbr111/python-bydhvs/sessions/89807568-498c-413a-9058-daaf24f1ddc3 Co-authored-by: bbr111 <46243805+bbr111@users.noreply.github.com> --- bydhvs/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bydhvs/__init__.py b/bydhvs/__init__.py index 0c2962c..5536034 100644 --- a/bydhvs/__init__.py +++ b/bydhvs/__init__.py @@ -698,6 +698,8 @@ def _is_modbus_exception_response(self, data: bytes, request: bytes) -> bool: or len(request) < self.MIN_MODBUS_REQUEST_LENGTH ): return False + if request[0] != self.MODBUS_ADDRESS: + return False if data[0] != self.MODBUS_ADDRESS: return False if data[1] != (request[1] | self.MODBUS_EXCEPTION_FLAG):