mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Fix NaN values in Modbus slaves sensors (#139969)
* Fix NaN values in Modbus slaves sensors * fixXbdraco
This commit is contained in:
parent
2dc2b0ffac
commit
b667fb2728
@ -285,10 +285,10 @@ class BaseStructPlatform(BasePlatform, RestoreEntity):
|
|||||||
v_result = []
|
v_result = []
|
||||||
for entry in val:
|
for entry in val:
|
||||||
v_temp = self.__process_raw_value(entry)
|
v_temp = self.__process_raw_value(entry)
|
||||||
if v_temp is None:
|
if self._data_type != DataType.CUSTOM:
|
||||||
v_result.append("0")
|
|
||||||
else:
|
|
||||||
v_result.append(str(v_temp))
|
v_result.append(str(v_temp))
|
||||||
|
else:
|
||||||
|
v_result.append(str(v_temp) if v_temp is not None else "0")
|
||||||
return ",".join(map(str, v_result))
|
return ",".join(map(str, v_result))
|
||||||
|
|
||||||
# Apply scale, precision, limits to floats and ints
|
# Apply scale, precision, limits to floats and ints
|
||||||
|
@ -73,7 +73,9 @@ class ModbusRegisterSensor(BaseStructPlatform, RestoreSensor, SensorEntity):
|
|||||||
super().__init__(hass, hub, entry)
|
super().__init__(hass, hub, entry)
|
||||||
if slave_count:
|
if slave_count:
|
||||||
self._count = self._count * (slave_count + 1)
|
self._count = self._count * (slave_count + 1)
|
||||||
self._coordinator: DataUpdateCoordinator[list[float] | None] | None = None
|
self._coordinator: DataUpdateCoordinator[list[float | None] | None] | None = (
|
||||||
|
None
|
||||||
|
)
|
||||||
self._attr_native_unit_of_measurement = entry.get(CONF_UNIT_OF_MEASUREMENT)
|
self._attr_native_unit_of_measurement = entry.get(CONF_UNIT_OF_MEASUREMENT)
|
||||||
self._attr_state_class = entry.get(CONF_STATE_CLASS)
|
self._attr_state_class = entry.get(CONF_STATE_CLASS)
|
||||||
self._attr_device_class = entry.get(CONF_DEVICE_CLASS)
|
self._attr_device_class = entry.get(CONF_DEVICE_CLASS)
|
||||||
@ -120,37 +122,45 @@ class ModbusRegisterSensor(BaseStructPlatform, RestoreSensor, SensorEntity):
|
|||||||
self._coordinator.async_set_updated_data(None)
|
self._coordinator.async_set_updated_data(None)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
return
|
return
|
||||||
|
self._attr_available = True
|
||||||
result = self.unpack_structure_result(raw_result.registers)
|
result = self.unpack_structure_result(raw_result.registers)
|
||||||
if self._coordinator:
|
if self._coordinator:
|
||||||
|
result_array: list[float | None] = []
|
||||||
if result:
|
if result:
|
||||||
result_array = list(
|
for i in result.split(","):
|
||||||
map(
|
if i != "None":
|
||||||
float if not self._value_is_int else int,
|
result_array.append(
|
||||||
result.split(","),
|
float(i) if not self._value_is_int else int(i)
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
result_array.append(None)
|
||||||
|
|
||||||
self._attr_native_value = result_array[0]
|
self._attr_native_value = result_array[0]
|
||||||
self._coordinator.async_set_updated_data(result_array)
|
self._coordinator.async_set_updated_data(result_array)
|
||||||
else:
|
else:
|
||||||
self._attr_native_value = None
|
self._attr_native_value = None
|
||||||
self._coordinator.async_set_updated_data(None)
|
result_array = (self._slave_count + 1) * [None]
|
||||||
|
self._coordinator.async_set_updated_data(result_array)
|
||||||
else:
|
else:
|
||||||
self._attr_native_value = result
|
self._attr_native_value = result
|
||||||
self._attr_available = self._attr_native_value is not None
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
class SlaveSensor(
|
class SlaveSensor(
|
||||||
CoordinatorEntity[DataUpdateCoordinator[list[float] | None]],
|
CoordinatorEntity[DataUpdateCoordinator[list[float | None] | None]],
|
||||||
RestoreSensor,
|
RestoreSensor,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
):
|
):
|
||||||
"""Modbus slave register sensor."""
|
"""Modbus slave register sensor."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return True if entity is available."""
|
||||||
|
return self._attr_available
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: DataUpdateCoordinator[list[float] | None],
|
coordinator: DataUpdateCoordinator[list[float | None] | None],
|
||||||
idx: int,
|
idx: int,
|
||||||
entry: dict[str, Any],
|
entry: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -178,4 +188,5 @@ class SlaveSensor(
|
|||||||
"""Handle updated data from the coordinator."""
|
"""Handle updated data from the coordinator."""
|
||||||
result = self.coordinator.data
|
result = self.coordinator.data
|
||||||
self._attr_native_value = result[self._idx] if result else None
|
self._attr_native_value = result[self._idx] if result else None
|
||||||
|
self._attr_available = result is not None
|
||||||
super()._handle_coordinator_update()
|
super()._handle_coordinator_update()
|
||||||
|
@ -428,7 +428,7 @@ async def test_config_wrong_struct_sensor(
|
|||||||
},
|
},
|
||||||
[0x89AB],
|
[0x89AB],
|
||||||
False,
|
False,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNKNOWN,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -631,7 +631,7 @@ async def test_config_wrong_struct_sensor(
|
|||||||
},
|
},
|
||||||
[0x8000, 0x0000],
|
[0x8000, 0x0000],
|
||||||
False,
|
False,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNKNOWN,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -742,7 +742,7 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
int.from_bytes(struct.pack(">f", float("nan"))[2:4]),
|
int.from_bytes(struct.pack(">f", float("nan"))[2:4]),
|
||||||
],
|
],
|
||||||
False,
|
False,
|
||||||
["34899771392.0", "0.0"],
|
["34899771392.0", STATE_UNKNOWN],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -757,7 +757,7 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
int.from_bytes(struct.pack(">f", float("nan"))[2:4]),
|
int.from_bytes(struct.pack(">f", float("nan"))[2:4]),
|
||||||
],
|
],
|
||||||
False,
|
False,
|
||||||
["34899771392.0", "0.0"],
|
["34899771392.0", STATE_UNKNOWN],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -802,7 +802,11 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
},
|
},
|
||||||
[0x0102, 0x0304, 0x0403, 0x0201, 0x0403],
|
[0x0102, 0x0304, 0x0403, 0x0201, 0x0403],
|
||||||
False,
|
False,
|
||||||
[STATE_UNAVAILABLE, STATE_UNKNOWN, STATE_UNKNOWN],
|
[
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -857,7 +861,7 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
},
|
},
|
||||||
[0x0102, 0x0304, 0x0403, 0x0201],
|
[0x0102, 0x0304, 0x0403, 0x0201],
|
||||||
True,
|
True,
|
||||||
[STATE_UNAVAILABLE, STATE_UNKNOWN],
|
[STATE_UNAVAILABLE, STATE_UNAVAILABLE],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -866,7 +870,7 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
},
|
},
|
||||||
[0x0102, 0x0304, 0x0403, 0x0201],
|
[0x0102, 0x0304, 0x0403, 0x0201],
|
||||||
True,
|
True,
|
||||||
[STATE_UNAVAILABLE, STATE_UNKNOWN],
|
[STATE_UNAVAILABLE, STATE_UNAVAILABLE],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -875,7 +879,7 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
False,
|
False,
|
||||||
[STATE_UNAVAILABLE, STATE_UNKNOWN],
|
[STATE_UNKNOWN, STATE_UNKNOWN],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -884,7 +888,35 @@ async def test_all_sensor(hass: HomeAssistant, mock_do_cycle, expected) -> None:
|
|||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
False,
|
False,
|
||||||
[STATE_UNAVAILABLE, STATE_UNKNOWN],
|
[STATE_UNKNOWN, STATE_UNKNOWN],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
CONF_VIRTUAL_COUNT: 4,
|
||||||
|
CONF_UNIQUE_ID: SLAVE_UNIQUE_ID,
|
||||||
|
CONF_DATA_TYPE: DataType.INT32,
|
||||||
|
CONF_NAN_VALUE: "0x800000",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
0x0,
|
||||||
|
0x35,
|
||||||
|
0x0,
|
||||||
|
0x38,
|
||||||
|
0x80,
|
||||||
|
0x0,
|
||||||
|
0x80,
|
||||||
|
0x0,
|
||||||
|
0xFFFF,
|
||||||
|
0xFFF6,
|
||||||
|
],
|
||||||
|
False,
|
||||||
|
[
|
||||||
|
"53",
|
||||||
|
"56",
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
"-10",
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -1103,7 +1135,7 @@ async def test_virtual_swap_sensor(
|
|||||||
)
|
)
|
||||||
async def test_wrong_unpack(hass: HomeAssistant, mock_do_cycle) -> None:
|
async def test_wrong_unpack(hass: HomeAssistant, mock_do_cycle) -> None:
|
||||||
"""Run test for sensor."""
|
"""Run test for sensor."""
|
||||||
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
|
assert hass.states.get(ENTITY_ID).state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -1131,14 +1163,14 @@ async def test_wrong_unpack(hass: HomeAssistant, mock_do_cycle) -> None:
|
|||||||
int.from_bytes(struct.pack(">f", float("nan"))[0:2]),
|
int.from_bytes(struct.pack(">f", float("nan"))[0:2]),
|
||||||
int.from_bytes(struct.pack(">f", float("nan"))[2:4]),
|
int.from_bytes(struct.pack(">f", float("nan"))[2:4]),
|
||||||
],
|
],
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNKNOWN,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
CONF_DATA_TYPE: DataType.FLOAT32,
|
CONF_DATA_TYPE: DataType.FLOAT32,
|
||||||
},
|
},
|
||||||
[0x6E61, 0x6E00],
|
[0x6E61, 0x6E00],
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNKNOWN,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -1147,7 +1179,7 @@ async def test_wrong_unpack(hass: HomeAssistant, mock_do_cycle) -> None:
|
|||||||
CONF_STRUCTURE: "4s",
|
CONF_STRUCTURE: "4s",
|
||||||
},
|
},
|
||||||
[0x6E61, 0x6E00],
|
[0x6E61, 0x6E00],
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNKNOWN,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user