diff --git a/esphome/components/modbus/__init__.py b/esphome/components/modbus/__init__.py index db7484e306..cad2781cc1 100644 --- a/esphome/components/modbus/__init__.py +++ b/esphome/components/modbus/__init__.py @@ -52,6 +52,12 @@ CONF_TURNAROUND_TIME = "turnaround_time" MODBUS_ROLES = ["client", "server"] +# The client hub takes these times as 16-bit milliseconds; a Modbus timeout is far shorter anyway. +_HUB_TIME_PERIOD = cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=cv.TimePeriod(milliseconds=65535)), +) + # The write (mutating) function codes, matching modbus::helpers::is_function_code_write(). 0x17 # (read/write multiple) is included: it mutates, so the hub treats it as a write despite its read half. @@ -273,12 +279,8 @@ CONFIG_SCHEMA = cv.typed_schema( { cv.GenerateID(): cv.declare_id(ModbusClient), cv.Optional(CONF_FLOW_CONTROL_PIN): pins.gpio_output_pin_schema, - cv.Optional( - CONF_SEND_WAIT_TIME, default="2000ms" - ): cv.positive_time_period_milliseconds, - cv.Optional( - CONF_TURNAROUND_TIME, default="600ms" - ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SEND_WAIT_TIME, default="2000ms"): _HUB_TIME_PERIOD, + cv.Optional(CONF_TURNAROUND_TIME, default="600ms"): _HUB_TIME_PERIOD, } ) .extend(cv.COMPONENT_SCHEMA) diff --git a/tests/component_tests/modbus/test_modbus.py b/tests/component_tests/modbus/test_modbus.py index 1eafb13166..d7e2c9ba2f 100644 --- a/tests/component_tests/modbus/test_modbus.py +++ b/tests/component_tests/modbus/test_modbus.py @@ -4,7 +4,11 @@ import pytest from esphome import config_validation as cv from esphome.components import modbus -from esphome.components.modbus import CONF_MODBUS_ID, _validate_server_address +from esphome.components.modbus import ( + _HUB_TIME_PERIOD, + CONF_MODBUS_ID, + _validate_server_address, +) from esphome.const import CONF_ADDRESS @@ -36,3 +40,13 @@ def test_client_schema_still_accepts_address_zero() -> None: # A client may address 0: writes are broadcast, and reads are allowed with allow_broadcast_read. schema = modbus.modbus_device_schema(0x01) assert schema({CONF_MODBUS_ID: "hub", CONF_ADDRESS: 0})[CONF_ADDRESS] == 0 + + +def test_hub_time_accepts_up_to_65535_ms() -> None: + assert _HUB_TIME_PERIOD("65535ms").total_milliseconds == 65535 + + +def test_hub_time_rejects_values_the_hub_would_truncate() -> None: + # The setters take 16-bit milliseconds: 70 s would silently become 4464 ms. + with pytest.raises(cv.Invalid): + _HUB_TIME_PERIOD("70s")