diff --git a/esphome/components/sx126x/packet_interface/__init__.py b/esphome/components/sx126x/packet_interface/__init__.py index 8af293d8a6a..117b2bc38e6 100644 --- a/esphome/components/sx126x/packet_interface/__init__.py +++ b/esphome/components/sx126x/packet_interface/__init__.py @@ -4,12 +4,15 @@ from esphome.components.packet_interface.radio_link import ( radio_link_final_validate, radio_link_schema, ) +import esphome.config_validation as cv from esphome.types import ConfigType from .. import ( + CONF_MODULATION, CONF_PAYLOAD_LENGTH, CONF_RX_START, CONF_SX126X_ID, + CONF_SYNC_VALUE, DOMAIN, SX126x, SX126xListener, @@ -25,8 +28,21 @@ SX126xPacketInterface = sx126x_ns.class_( CONFIG_SCHEMA = radio_link_schema(SX126xPacketInterface, CONF_SX126X_ID, SX126x) + +def _check_fsk(config: ConfigType, radio: ConfigType) -> None: + """Without LoRa, the radio can tell that a packet is arriving only from its sync word, which listen before talk + needs. + """ + if radio[CONF_MODULATION] != "LORA" and not radio[CONF_SYNC_VALUE]: + raise cv.Invalid( + f"Without LoRa modulation the radio needs a '{CONF_SYNC_VALUE}', so that the link can tell when a packet " + "is arriving and not send over it", + path=[CONF_SX126X_ID], + ) + + FINAL_VALIDATE_SCHEMA = radio_link_final_validate( - CONF_SX126X_ID, CONF_PAYLOAD_LENGTH, CONF_RX_START + CONF_SX126X_ID, CONF_PAYLOAD_LENGTH, CONF_RX_START, _check_fsk ) diff --git a/tests/unit_tests/test_lora_packet_interface.py b/tests/unit_tests/test_lora_packet_interface.py index 27140a42729..fc389430b91 100644 --- a/tests/unit_tests/test_lora_packet_interface.py +++ b/tests/unit_tests/test_lora_packet_interface.py @@ -90,7 +90,7 @@ class _FullConfig(dict): ( sx126x_packet_interface, "sx126x", - {"payload_length": 10}, + _lora(payload_length=10), {}, "payload_length", {"payload_length": 0}, @@ -143,6 +143,14 @@ class _FullConfig(dict): "sync_value", {"preamble_detect": 2}, ), + ( + sx126x_packet_interface, + "sx126x", + {"modulation": "FSK", "payload_length": 0, "sync_value": []}, + {}, + "sync_value", + {"sync_value": [0x12, 0x34]}, + ), ], ) def test_radio_settings_checked( @@ -154,7 +162,7 @@ def test_radio_settings_checked( fix: dict | None, ) -> None: """Packets vary in length; FSK or OOK on an SX127x needs packet mode, a sync word or preamble detection, and - holds at most 64 bytes. + holds at most 64 bytes; FSK on an SX126x needs a sync word. """ config = {f"{domain}_id": "radio", CONF_MAX_PACKET_SIZE: 64, **link} token = fv.full_config.set(_FullConfig(domain, radio)) @@ -190,7 +198,7 @@ def test_radio_settings_checked( ) def test_rx_start_needed_for_replies(setup_core, link: dict, match: str | None) -> None: """A radio that sleeps after sending suits only a link whose layers need no packets back.""" - sleeping = {"payload_length": 0, "rx_start": False} + sleeping = _lora(payload_length=0, rx_start=False) config = sx126x_packet_interface.CONFIG_SCHEMA({"sx126x_id": "radio", **link}) token = fv.full_config.set(_FullConfig("sx126x", sleeping)) try: