Skip to content

Commit 30fc0b9

Browse files
committed
Fix NEXA and NOAH 0x0103 holding register dump parsing crash
1 parent 6ece2f3 commit 30fc0b9

3 files changed

Lines changed: 35 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## v2.7.3
2+
3+
### Bug Fixes
4+
+ Fixed NEXA and NOAH devices crashing the parser every hour when sending holding register dumps (message type 0x0103). These messages are now properly decoded and their sensor values are published to Home Assistant (#198)
5+
16
## v2.7.2
27

38
### New Features

grobro/grobro/client.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from grobro.grobro.builder import append_crc
2121
from grobro.grobro.builder import scramble
2222
from grobro.model.modbus_function import GrowattModbusFunctionSingle
23+
from grobro.model.modbus_message import GrowattModbusBlock
2324
from grobro.model.modbus_message import GrowattModbusFunction
2425
from grobro.model.modbus_message import GrowattModbusMessage
2526
from grobro.model.mqtt_config import MQTTConfig
@@ -367,17 +368,35 @@ def __on_message(self, client, userdata, msg: MQTTMessage):
367368
self._client.publish(topic, eco["data"], retain=PUBLISH_SENSORS_RETAINED)
368369
return
369370

371+
modbus_message = None
372+
370373
# NOAH-specific message types (FE19 config, 0103 holding regs, etc.)
371374
noah_msg = parser.parse_noah_message(unscrambled)
372-
if noah_msg and noah_msg.get("message_type") == 0xFE19 and device_id.startswith("0PVP"):
373-
config = noah_msg.get("config")
374-
if config and config.serial_number:
375-
LOG.info("Received NOAH config for %s (sw_version=%s)", config.serial_number, config.sw_version or "?")
376-
self.on_config(device_id, config)
377-
return
375+
if noah_msg:
376+
if noah_msg.get("message_type") == 0xFE19 and device_id.startswith("0PVP"):
377+
config = noah_msg.get("config")
378+
if config and config.serial_number:
379+
LOG.info("Received NOAH config for %s (sw_version=%s)", config.serial_number, config.sw_version or "?")
380+
self.on_config(device_id, config)
381+
return
382+
383+
if noah_msg.get("message_type") == 0x0103:
384+
registers = noah_msg.get("registers", [])
385+
if registers:
386+
block = GrowattModbusBlock(
387+
start=0,
388+
end=len(registers) - 1,
389+
values=b"".join(struct.pack(">H", v) for v in registers),
390+
)
391+
modbus_message = GrowattModbusMessage(
392+
unknown=0,
393+
device_id=noah_msg["device_id"],
394+
function=GrowattModbusFunction.READ_SINGLE_REGISTER,
395+
register_blocks=[block],
396+
)
378397

379-
# Generic modbus message
380-
modbus_message = GrowattModbusMessage.parse_grobro(unscrambled)
398+
if modbus_message is None:
399+
modbus_message = GrowattModbusMessage.parse_grobro(unscrambled)
381400
LOG.debug("Received modbus message: %s", modbus_message)
382401

383402
if modbus_message:

grobro/model/modbus_message.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ def parse_grobro(buffer) -> Optional["GrowattModbusMessage"]:
169169

170170
while len(buffer) > offset + 6:
171171
block = GrowattModbusBlock.parse_grobro(buffer[offset:])
172+
if block is None:
173+
LOG.warning("Failed to parse modbus block at offset %d", offset)
174+
break
172175
register_blocks.append(block)
173176
offset += block.size()
174177

0 commit comments

Comments
 (0)