Skip to content

Commit a6c0fd5

Browse files
committed
Implement pymodbus version compatibility handling and update method calls to use 'device_id' for version 3.10+
1 parent ca893b3 commit a6c0fd5

File tree

1 file changed

+92
-15
lines changed

1 file changed

+92
-15
lines changed

custom_components/sigen/modbus.py

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,91 @@
4141
DC_CHARGER_RUNNING_INFO_REGISTERS,
4242
DC_CHARGER_PARAMETER_REGISTERS,
4343
)
44-
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
45-
from .common import generate_device_id
46-
from .const import DOMAIN
47-
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
48-
from .common import generate_device_id
49-
from .const import DOMAIN
5044

5145
_LOGGER = logging.getLogger(__name__)
5246

47+
# Pymodbus version compatibility handling
48+
def _get_pymodbus_version_info():
49+
"""Get pymodbus version information for compatibility handling."""
50+
try:
51+
# Parse version string like "3.6.8" or "3.10.0"
52+
version_parts = pymodbus_version.split('.')
53+
major = int(version_parts[0])
54+
minor = int(version_parts[1])
55+
return major, minor
56+
except (ValueError, IndexError):
57+
# Fallback to assuming older version if parsing fails
58+
_LOGGER.warning("Could not parse pymodbus version '%s', assuming < 3.10", pymodbus_version)
59+
return 3, 0
60+
61+
# Check if we need to use 'device_id' instead of 'slave'
62+
_PYMODBUS_MAJOR, _PYMODBUS_MINOR = _get_pymodbus_version_info()
63+
_USE_DEVICE_ID = (_PYMODBUS_MAJOR > 3) or (_PYMODBUS_MAJOR == 3 and _PYMODBUS_MINOR >= 10)
64+
65+
def _call_modbus_method_safe(client_method, *args, **kwargs):
66+
"""
67+
Call a pymodbus client method with version-compatible parameter handling.
68+
69+
This function handles the parameter name change from 'slave' to 'device_id'
70+
that occurred in pymodbus 3.10+.
71+
72+
Args:
73+
client_method: The pymodbus client method to call
74+
*args: Positional arguments
75+
**kwargs: Keyword arguments, including 'slave' or 'device_id'
76+
77+
Returns:
78+
The result of the client method call
79+
80+
Raises:
81+
TypeError: If the method call fails due to parameter incompatibility
82+
Other exceptions from the underlying method
83+
"""
84+
if _USE_DEVICE_ID:
85+
# For pymodbus >= 3.10, use 'device_id' parameter
86+
if 'slave' in kwargs:
87+
kwargs['device_id'] = kwargs.pop('slave')
88+
89+
try:
90+
return client_method(*args, **kwargs)
91+
except TypeError as e:
92+
if 'device_id' in str(e):
93+
# If device_id also fails, try falling back to positional args
94+
_LOGGER.debug("device_id parameter failed, trying positional arguments")
95+
# Remove device_id and try with positional args if possible
96+
kwargs_copy = kwargs.copy()
97+
if 'device_id' in kwargs_copy:
98+
device_id = kwargs_copy.pop('device_id')
99+
# Try calling with device_id as positional argument
100+
try:
101+
return client_method(*args, device_id, **kwargs_copy)
102+
except TypeError as inner_e:
103+
# If positional also fails, re-raise original error
104+
raise e from inner_e
105+
else:
106+
raise e
107+
else:
108+
raise e
109+
else:
110+
# For pymodbus < 3.10, use 'slave' parameter
111+
try:
112+
return client_method(*args, **kwargs)
113+
except TypeError as e:
114+
if 'slave' in str(e):
115+
# If slave fails, try falling back to positional args
116+
_LOGGER.debug("slave parameter failed, trying positional arguments")
117+
kwargs_copy = kwargs.copy()
118+
if 'slave' in kwargs_copy:
119+
slave = kwargs_copy.pop('slave')
120+
try:
121+
return client_method(*args, slave, **kwargs_copy)
122+
except TypeError as inner_e:
123+
raise e from inner_e
124+
else:
125+
raise e
126+
else:
127+
raise e
128+
53129
@dataclass
54130
class ModbusConnectionConfig:
55131
"""Configuration for a Modbus connection."""
@@ -293,13 +369,15 @@ async def _probe_single_register(
293369

294370
with _suppress_pymodbus_logging(really_suppress= False if _LOGGER.isEnabledFor(logging.DEBUG) else True):
295371
if register.register_type == RegisterType.READ_ONLY:
296-
result = await client.read_input_registers(
372+
result = await _call_modbus_method_safe(
373+
client.read_input_registers,
297374
address=register.address,
298375
count=register.count,
299376
slave=slave_id
300377
)
301378
elif register.register_type == RegisterType.HOLDING:
302-
result = await client.read_holding_registers(
379+
result = await _call_modbus_method_safe(
380+
client.read_holding_registers,
303381
address=register.address,
304382
count=register.count,
305383
slave=slave_id
@@ -363,7 +441,6 @@ async def async_probe_registers(
363441
_LOGGER.debug("No registers need probing for %s.", device_info_log)
364442
# If no probing is needed, still generate intervals from already known registers
365443
# This handles the case where probing was already done in a previous run.
366-
pass
367444
else:
368445
_LOGGER.debug("Probing %d registers concurrently for %s...", len(tasks), device_info_log)
369446

@@ -501,10 +578,8 @@ async def async_read_registers(
501578

502579
async with self._locks[key]:
503580
with _suppress_pymodbus_logging(really_suppress= False if _LOGGER.isEnabledFor(logging.DEBUG) else True):
504-
result = await client.read_input_registers(
505-
address=address, count=count, slave=slave_id
506-
) if register_type == RegisterType.READ_ONLY \
507-
else await client.read_holding_registers(
581+
result = await _call_modbus_method_safe(
582+
client.read_input_registers if register_type == RegisterType.READ_ONLY else client.read_holding_registers,
508583
address=address, count=count, slave=slave_id
509584
)
510585

@@ -590,13 +665,15 @@ async def async_write_register(
590665
)
591666

592667
if approach["function"] == "write_registers":
593-
result = await client.write_registers(
668+
result = await _call_modbus_method_safe(
669+
client.write_registers,
594670
address=approach["address"],
595671
values=approach["values"],
596672
slave=slave_id
597673
)
598674
else: # write_register
599-
result = await client.write_register(
675+
result = await _call_modbus_method_safe(
676+
client.write_register,
600677
address=approach["address"],
601678
value=approach["value"],
602679
slave=slave_id

0 commit comments

Comments
 (0)