Report modbus buffer too small or too big to unpack (#57838)

pull/58138/head
jan iversen 2021-10-21 00:22:01 +02:00 committed by GitHub
parent 38586d2cf1
commit cca7da77ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 4 deletions

View File

@ -160,7 +160,7 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
registers.reverse()
return registers
def unpack_structure_result(self, registers: list[int]) -> str:
def unpack_structure_result(self, registers: list[int]) -> str | None:
"""Convert registers to proper result."""
registers = self._swap_registers(registers)
@ -168,7 +168,13 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
if self._data_type == DataType.STRING:
return byte_string.decode()
val = struct.unpack(self._structure, byte_string)
try:
val = struct.unpack(self._structure, byte_string)
except struct.error as err:
recv_size = len(registers) * 2
msg = f"Received {recv_size} bytes, unpack error {err}"
_LOGGER.error(msg)
return None
# Issue: https://github.com/home-assistant/core/issues/41944
# If unpack() returns a tuple greater than 1, don't try to process the value.
# Instead, return the values of unpack(...) separated by commas.

View File

@ -166,7 +166,8 @@ class ModbusThermostat(BaseStructPlatform, RestoreEntity, ClimateEntity):
self._lazy_errors = self._lazy_error_count
self._value = self.unpack_structure_result(result.registers)
self._attr_available = True
if not self._value:
self._attr_available = False
return None
self._attr_available = True
return float(self._value)

View File

@ -74,6 +74,9 @@ class ModbusRegisterSensor(BaseStructPlatform, RestoreEntity, SensorEntity):
return
self._attr_native_value = self.unpack_structure_result(result.registers)
if self._attr_native_value is None:
self._attr_available = False
else:
self._attr_available = True
self._lazy_errors = self._lazy_error_count
self._attr_available = True
self.async_write_ha_state()

View File

@ -94,11 +94,13 @@ async def test_temperature_climate(hass, expected, mock_do_cycle):
{
CONF_CLIMATES: [
{
CONF_COUNT: 2,
CONF_NAME: TEST_ENTITY_NAME,
CONF_TARGET_TEMP: 117,
CONF_ADDRESS: 117,
CONF_SLAVE: 10,
CONF_SCAN_INTERVAL: 0,
CONF_DATA_TYPE: DataType.INT32,
}
]
},

View File

@ -559,6 +559,44 @@ async def test_all_sensor(hass, mock_do_cycle, expected):
assert hass.states.get(ENTITY_ID).state == expected
@pytest.mark.parametrize(
"do_config",
[
{
CONF_SENSORS: [
{
CONF_NAME: TEST_ENTITY_NAME,
CONF_ADDRESS: 51,
CONF_SCAN_INTERVAL: 1,
},
],
},
],
)
@pytest.mark.parametrize(
"config_addon,register_words",
[
(
{
CONF_COUNT: 1,
CONF_DATA_TYPE: DataType.INT16,
},
[7, 9],
),
(
{
CONF_COUNT: 2,
CONF_DATA_TYPE: DataType.INT32,
},
[7],
),
],
)
async def test_wrong_unpack(hass, mock_do_cycle):
"""Run test for sensor."""
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
@pytest.mark.parametrize(
"do_config",
[