Allow sending GroupValueResponse telegrams with knx.send service (#62639)
* Add knx.respond service * Combine knx.send and knx.respond services * Rename knx attribute and fix tests * Use parametrization in tests Co-authored-by: Marvin Wichmann <marvin.wichmann@unic.com>
This commit is contained in:
parent
7185e1140d
commit
ab4effc7e2
4 changed files with 119 additions and 41 deletions
|
@ -82,6 +82,7 @@ CONF_KNX_EVENT_FILTER: Final = "event_filter"
|
||||||
SERVICE_KNX_SEND: Final = "send"
|
SERVICE_KNX_SEND: Final = "send"
|
||||||
SERVICE_KNX_ATTR_PAYLOAD: Final = "payload"
|
SERVICE_KNX_ATTR_PAYLOAD: Final = "payload"
|
||||||
SERVICE_KNX_ATTR_TYPE: Final = "type"
|
SERVICE_KNX_ATTR_TYPE: Final = "type"
|
||||||
|
SERVICE_KNX_ATTR_RESPONSE: Final = "response"
|
||||||
SERVICE_KNX_ATTR_REMOVE: Final = "remove"
|
SERVICE_KNX_ATTR_REMOVE: Final = "remove"
|
||||||
SERVICE_KNX_EVENT_REGISTER: Final = "event_register"
|
SERVICE_KNX_EVENT_REGISTER: Final = "event_register"
|
||||||
SERVICE_KNX_EXPOSURE_REGISTER: Final = "exposure_register"
|
SERVICE_KNX_EXPOSURE_REGISTER: Final = "exposure_register"
|
||||||
|
@ -142,6 +143,7 @@ SERVICE_KNX_SEND_SCHEMA = vol.Any(
|
||||||
),
|
),
|
||||||
vol.Required(SERVICE_KNX_ATTR_PAYLOAD): cv.match_all,
|
vol.Required(SERVICE_KNX_ATTR_PAYLOAD): cv.match_all,
|
||||||
vol.Required(SERVICE_KNX_ATTR_TYPE): sensor_type_validator,
|
vol.Required(SERVICE_KNX_ATTR_TYPE): sensor_type_validator,
|
||||||
|
vol.Optional(SERVICE_KNX_ATTR_RESPONSE, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
vol.Schema(
|
vol.Schema(
|
||||||
|
@ -154,6 +156,7 @@ SERVICE_KNX_SEND_SCHEMA = vol.Any(
|
||||||
vol.Required(SERVICE_KNX_ATTR_PAYLOAD): vol.Any(
|
vol.Required(SERVICE_KNX_ATTR_PAYLOAD): vol.Any(
|
||||||
cv.positive_int, [cv.positive_int]
|
cv.positive_int, [cv.positive_int]
|
||||||
),
|
),
|
||||||
|
vol.Optional(SERVICE_KNX_ATTR_RESPONSE, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -551,6 +554,7 @@ class KNXModule:
|
||||||
attr_address = call.data[KNX_ADDRESS]
|
attr_address = call.data[KNX_ADDRESS]
|
||||||
attr_payload = call.data[SERVICE_KNX_ATTR_PAYLOAD]
|
attr_payload = call.data[SERVICE_KNX_ATTR_PAYLOAD]
|
||||||
attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE)
|
attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE)
|
||||||
|
attr_response = call.data[SERVICE_KNX_ATTR_RESPONSE]
|
||||||
|
|
||||||
payload: DPTBinary | DPTArray
|
payload: DPTBinary | DPTArray
|
||||||
if attr_type is not None:
|
if attr_type is not None:
|
||||||
|
@ -566,7 +570,9 @@ class KNXModule:
|
||||||
for address in attr_address:
|
for address in attr_address:
|
||||||
telegram = Telegram(
|
telegram = Telegram(
|
||||||
destination_address=parse_device_group_address(address),
|
destination_address=parse_device_group_address(address),
|
||||||
payload=GroupValueWrite(payload),
|
payload=GroupValueResponse(payload)
|
||||||
|
if attr_response
|
||||||
|
else GroupValueWrite(payload),
|
||||||
)
|
)
|
||||||
await self.xknx.telegrams.put(telegram)
|
await self.xknx.telegrams.put(telegram)
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,12 @@ send:
|
||||||
example: "temperature"
|
example: "temperature"
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
response:
|
||||||
|
name: "Send as Response"
|
||||||
|
description: "If set to `True`, the telegram will be sent as a `GroupValueResponse` instead of a `GroupValueWrite`."
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
read:
|
read:
|
||||||
name: "Read from KNX bus"
|
name: "Read from KNX bus"
|
||||||
description: "Send GroupValueRead requests to the KNX bus. Response can be used from `knx_event` and will be processed in KNX entities."
|
description: "Send GroupValueRead requests to the KNX bus. Response can be used from `knx_event` and will be processed in KNX entities."
|
||||||
|
|
|
@ -109,7 +109,7 @@ class KNXTestKit:
|
||||||
# APCI Service tests
|
# APCI Service tests
|
||||||
####################
|
####################
|
||||||
|
|
||||||
async def _assert_telegram(
|
async def assert_telegram(
|
||||||
self,
|
self,
|
||||||
group_address: str,
|
group_address: str,
|
||||||
payload: int | tuple[int, ...] | None,
|
payload: int | tuple[int, ...] | None,
|
||||||
|
@ -141,19 +141,19 @@ class KNXTestKit:
|
||||||
|
|
||||||
async def assert_read(self, group_address: str) -> None:
|
async def assert_read(self, group_address: str) -> None:
|
||||||
"""Assert outgoing GroupValueRead telegram. One by one in timely order."""
|
"""Assert outgoing GroupValueRead telegram. One by one in timely order."""
|
||||||
await self._assert_telegram(group_address, None, GroupValueRead)
|
await self.assert_telegram(group_address, None, GroupValueRead)
|
||||||
|
|
||||||
async def assert_response(
|
async def assert_response(
|
||||||
self, group_address: str, payload: int | tuple[int, ...]
|
self, group_address: str, payload: int | tuple[int, ...]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Assert outgoing GroupValueResponse telegram. One by one in timely order."""
|
"""Assert outgoing GroupValueResponse telegram. One by one in timely order."""
|
||||||
await self._assert_telegram(group_address, payload, GroupValueResponse)
|
await self.assert_telegram(group_address, payload, GroupValueResponse)
|
||||||
|
|
||||||
async def assert_write(
|
async def assert_write(
|
||||||
self, group_address: str, payload: int | tuple[int, ...]
|
self, group_address: str, payload: int | tuple[int, ...]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Assert outgoing GroupValueWrite telegram. One by one in timely order."""
|
"""Assert outgoing GroupValueWrite telegram. One by one in timely order."""
|
||||||
await self._assert_telegram(group_address, payload, GroupValueWrite)
|
await self.assert_telegram(group_address, payload, GroupValueWrite)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Incoming telegrams
|
# Incoming telegrams
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
"""Test KNX services."""
|
"""Test KNX services."""
|
||||||
|
import pytest
|
||||||
|
from xknx.telegram.apci import GroupValueResponse, GroupValueWrite
|
||||||
|
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
@ -7,51 +10,114 @@ from .conftest import KNXTestKit
|
||||||
from tests.common import async_capture_events
|
from tests.common import async_capture_events
|
||||||
|
|
||||||
|
|
||||||
async def test_send(hass: HomeAssistant, knx: KNXTestKit):
|
@pytest.mark.parametrize(
|
||||||
|
"service_payload,expected_telegrams,expected_apci",
|
||||||
|
[
|
||||||
|
# send DPT 1 telegram
|
||||||
|
(
|
||||||
|
{"address": "1/2/3", "payload": True, "response": True},
|
||||||
|
[("1/2/3", True)],
|
||||||
|
GroupValueResponse,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"address": "1/2/3", "payload": True, "response": False},
|
||||||
|
[("1/2/3", True)],
|
||||||
|
GroupValueWrite,
|
||||||
|
),
|
||||||
|
# send DPT 5 telegram
|
||||||
|
(
|
||||||
|
{"address": "1/2/3", "payload": [99], "response": True},
|
||||||
|
[("1/2/3", (99,))],
|
||||||
|
GroupValueResponse,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"address": "1/2/3", "payload": [99], "response": False},
|
||||||
|
[("1/2/3", (99,))],
|
||||||
|
GroupValueWrite,
|
||||||
|
),
|
||||||
|
# send DPT 5 percent telegram
|
||||||
|
(
|
||||||
|
{"address": "1/2/3", "payload": 99, "type": "percent", "response": True},
|
||||||
|
[("1/2/3", (0xFC,))],
|
||||||
|
GroupValueResponse,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"address": "1/2/3", "payload": 99, "type": "percent", "response": False},
|
||||||
|
[("1/2/3", (0xFC,))],
|
||||||
|
GroupValueWrite,
|
||||||
|
),
|
||||||
|
# send temperature DPT 9 telegram
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"address": "1/2/3",
|
||||||
|
"payload": 21.0,
|
||||||
|
"type": "temperature",
|
||||||
|
"response": True,
|
||||||
|
},
|
||||||
|
[("1/2/3", (0x0C, 0x1A))],
|
||||||
|
GroupValueResponse,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"address": "1/2/3",
|
||||||
|
"payload": 21.0,
|
||||||
|
"type": "temperature",
|
||||||
|
"response": False,
|
||||||
|
},
|
||||||
|
[("1/2/3", (0x0C, 0x1A))],
|
||||||
|
GroupValueWrite,
|
||||||
|
),
|
||||||
|
# send multiple telegrams
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"address": ["1/2/3", "2/2/2", "3/3/3"],
|
||||||
|
"payload": 99,
|
||||||
|
"type": "percent",
|
||||||
|
"response": True,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
("1/2/3", (0xFC,)),
|
||||||
|
("2/2/2", (0xFC,)),
|
||||||
|
("3/3/3", (0xFC,)),
|
||||||
|
],
|
||||||
|
GroupValueResponse,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"address": ["1/2/3", "2/2/2", "3/3/3"],
|
||||||
|
"payload": 99,
|
||||||
|
"type": "percent",
|
||||||
|
"response": False,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
("1/2/3", (0xFC,)),
|
||||||
|
("2/2/2", (0xFC,)),
|
||||||
|
("3/3/3", (0xFC,)),
|
||||||
|
],
|
||||||
|
GroupValueWrite,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_send(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
knx: KNXTestKit,
|
||||||
|
service_payload,
|
||||||
|
expected_telegrams,
|
||||||
|
expected_apci,
|
||||||
|
):
|
||||||
"""Test `knx.send` service."""
|
"""Test `knx.send` service."""
|
||||||
test_address = "1/2/3"
|
|
||||||
await knx.setup_integration({})
|
await knx.setup_integration({})
|
||||||
|
|
||||||
# send DPT 1 telegram
|
|
||||||
await hass.services.async_call(
|
|
||||||
"knx", "send", {"address": test_address, "payload": True}, blocking=True
|
|
||||||
)
|
|
||||||
await knx.assert_write(test_address, True)
|
|
||||||
|
|
||||||
# send raw DPT 5 telegram
|
|
||||||
await hass.services.async_call(
|
|
||||||
"knx", "send", {"address": test_address, "payload": [99]}, blocking=True
|
|
||||||
)
|
|
||||||
await knx.assert_write(test_address, (99,))
|
|
||||||
|
|
||||||
# send "percent" DPT 5 telegram
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"knx",
|
"knx",
|
||||||
"send",
|
"send",
|
||||||
{"address": test_address, "payload": 99, "type": "percent"},
|
service_payload,
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await knx.assert_write(test_address, (0xFC,))
|
|
||||||
|
|
||||||
# send "temperature" DPT 9 telegram
|
for expected_response in expected_telegrams:
|
||||||
await hass.services.async_call(
|
group_address, payload = expected_response
|
||||||
"knx",
|
await knx.assert_telegram(group_address, payload, expected_apci)
|
||||||
"send",
|
|
||||||
{"address": test_address, "payload": 21.0, "type": "temperature"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
await knx.assert_write(test_address, (0x0C, 0x1A))
|
|
||||||
|
|
||||||
# send multiple telegrams
|
|
||||||
await hass.services.async_call(
|
|
||||||
"knx",
|
|
||||||
"send",
|
|
||||||
{"address": [test_address, "2/2/2", "3/3/3"], "payload": 99, "type": "percent"},
|
|
||||||
blocking=True,
|
|
||||||
)
|
|
||||||
await knx.assert_write(test_address, (0xFC,))
|
|
||||||
await knx.assert_write("2/2/2", (0xFC,))
|
|
||||||
await knx.assert_write("3/3/3", (0xFC,))
|
|
||||||
|
|
||||||
|
|
||||||
async def test_read(hass: HomeAssistant, knx: KNXTestKit):
|
async def test_read(hass: HomeAssistant, knx: KNXTestKit):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue