Handle KNX expose conversion exceptions and unavailable states (#124776)
parent
163795e73a
commit
633ff0ea42
|
@ -125,6 +125,8 @@ class KNXExposeSensor:
|
|||
def _get_expose_value(self, state: State | None) -> bool | int | float | str | None:
|
||||
"""Extract value from state."""
|
||||
if state is None or state.state in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
||||
if self.expose_default is None:
|
||||
return None
|
||||
value = self.expose_default
|
||||
elif self.expose_attribute is not None:
|
||||
_attr = state.attributes.get(self.expose_attribute)
|
||||
|
@ -154,12 +156,22 @@ class KNXExposeSensor:
|
|||
if value is not None and (
|
||||
isinstance(self.device.sensor_value, RemoteValueSensor)
|
||||
):
|
||||
if issubclass(self.device.sensor_value.dpt_class, DPTNumeric):
|
||||
return float(value)
|
||||
if issubclass(self.device.sensor_value.dpt_class, DPTString):
|
||||
# DPT 16.000 only allows up to 14 Bytes
|
||||
return str(value)[:14]
|
||||
return value
|
||||
try:
|
||||
if issubclass(self.device.sensor_value.dpt_class, DPTNumeric):
|
||||
return float(value)
|
||||
if issubclass(self.device.sensor_value.dpt_class, DPTString):
|
||||
# DPT 16.000 only allows up to 14 Bytes
|
||||
return str(value)[:14]
|
||||
except (ValueError, TypeError) as err:
|
||||
_LOGGER.warning(
|
||||
'Could not expose %s %s value "%s" to KNX: Conversion failed: %s',
|
||||
self.entity_id,
|
||||
self.expose_attribute or "state",
|
||||
value,
|
||||
err,
|
||||
)
|
||||
return None
|
||||
return value # type: ignore[no-any-return]
|
||||
|
||||
async def _async_entity_changed(self, event: Event[EventStateChangedData]) -> None:
|
||||
"""Handle entity change."""
|
||||
|
|
|
@ -108,6 +108,11 @@ async def test_expose_attribute(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
|||
await hass.async_block_till_done()
|
||||
await knx.assert_telegram_count(0)
|
||||
|
||||
# Ignore "unavailable" state
|
||||
hass.states.async_set(entity_id, "unavailable", {attribute: None})
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_telegram_count(0)
|
||||
|
||||
|
||||
async def test_expose_attribute_with_default(
|
||||
hass: HomeAssistant, knx: KNXTestKit
|
||||
|
@ -131,7 +136,7 @@ async def test_expose_attribute_with_default(
|
|||
await knx.receive_read("1/1/8")
|
||||
await knx.assert_response("1/1/8", (0,))
|
||||
|
||||
# Change state to "on"; no attribute
|
||||
# Change state to "on"; no attribute -> default
|
||||
hass.states.async_set(entity_id, "on", {})
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_write("1/1/8", (0,))
|
||||
|
@ -146,6 +151,11 @@ async def test_expose_attribute_with_default(
|
|||
await hass.async_block_till_done()
|
||||
await knx.assert_no_telegram()
|
||||
|
||||
# Use default for "unavailable" state
|
||||
hass.states.async_set(entity_id, "unavailable")
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_write("1/1/8", (0,))
|
||||
|
||||
# Change state and attribute
|
||||
hass.states.async_set(entity_id, "on", {attribute: 3})
|
||||
await hass.async_block_till_done()
|
||||
|
@ -290,8 +300,18 @@ async def test_expose_value_template(
|
|||
assert "Error rendering value template for KNX expose" in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"invalid_attribute",
|
||||
[
|
||||
101.0,
|
||||
"invalid", # can't cast to float
|
||||
],
|
||||
)
|
||||
async def test_expose_conversion_exception(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, knx: KNXTestKit
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
knx: KNXTestKit,
|
||||
invalid_attribute: str,
|
||||
) -> None:
|
||||
"""Test expose throws exception."""
|
||||
|
||||
|
@ -313,16 +333,17 @@ async def test_expose_conversion_exception(
|
|||
await knx.receive_read("1/1/8")
|
||||
await knx.assert_response("1/1/8", (3,))
|
||||
|
||||
caplog.clear()
|
||||
# Change attribute: Expect no exception
|
||||
hass.states.async_set(
|
||||
entity_id,
|
||||
"on",
|
||||
{attribute: 101},
|
||||
{attribute: invalid_attribute},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await knx.assert_no_telegram()
|
||||
assert (
|
||||
'Could not expose fake.entity fake_attribute value "101.0" to KNX:'
|
||||
f'Could not expose fake.entity fake_attribute value "{invalid_attribute}" to KNX:'
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue