Handle relation between scan_interval and pymodbus timeout in modbus (#50363)
* Control scan_interval compared to pymodbus timeout. add MINIMUM_SCAN_INTERVAL=5 seconds and validata with Voluptous. Keep modbus.py 100% coverage. * Please pylint. * Review comments. * pylint.
This commit is contained in:
parent
9ae021a284
commit
70b09ed9a1
4 changed files with 70 additions and 29 deletions
|
@ -1,6 +1,7 @@
|
||||||
"""Support for Modbus."""
|
"""Support for Modbus."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
@ -95,10 +96,14 @@ from .const import (
|
||||||
DEFAULT_SCAN_INTERVAL,
|
DEFAULT_SCAN_INTERVAL,
|
||||||
DEFAULT_STRUCTURE_PREFIX,
|
DEFAULT_STRUCTURE_PREFIX,
|
||||||
DEFAULT_TEMP_UNIT,
|
DEFAULT_TEMP_UNIT,
|
||||||
|
MINIMUM_SCAN_INTERVAL,
|
||||||
MODBUS_DOMAIN as DOMAIN,
|
MODBUS_DOMAIN as DOMAIN,
|
||||||
|
PLATFORMS,
|
||||||
)
|
)
|
||||||
from .modbus import modbus_setup
|
from .modbus import modbus_setup
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
BASE_SCHEMA = vol.Schema({vol.Optional(CONF_NAME, default=DEFAULT_HUB): cv.string})
|
BASE_SCHEMA = vol.Schema({vol.Optional(CONF_NAME, default=DEFAULT_HUB): cv.string})
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,6 +126,38 @@ def number(value: Any) -> int | float:
|
||||||
raise vol.Invalid(f"invalid number {value}") from err
|
raise vol.Invalid(f"invalid number {value}") from err
|
||||||
|
|
||||||
|
|
||||||
|
def control_scan_interval(config: dict) -> dict:
|
||||||
|
"""Control scan_interval."""
|
||||||
|
for hub in config:
|
||||||
|
minimum_scan_interval = DEFAULT_SCAN_INTERVAL
|
||||||
|
for component, conf_key in PLATFORMS:
|
||||||
|
if conf_key not in hub:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for entry in hub[conf_key]:
|
||||||
|
scan_interval = entry.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||||
|
if scan_interval < MINIMUM_SCAN_INTERVAL:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"%s %s scan_interval(%d) is adjusted to minimum(%d)",
|
||||||
|
component,
|
||||||
|
entry.get(CONF_NAME),
|
||||||
|
scan_interval,
|
||||||
|
MINIMUM_SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
scan_interval = MINIMUM_SCAN_INTERVAL
|
||||||
|
entry[CONF_SCAN_INTERVAL] = scan_interval
|
||||||
|
minimum_scan_interval = min(scan_interval, minimum_scan_interval)
|
||||||
|
if CONF_TIMEOUT in hub and hub[CONF_TIMEOUT] > minimum_scan_interval - 1:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Modbus %s timeout(%d) is adjusted(%d) due to scan_interval",
|
||||||
|
hub.get(CONF_NAME, ""),
|
||||||
|
hub[CONF_TIMEOUT],
|
||||||
|
minimum_scan_interval - 1,
|
||||||
|
)
|
||||||
|
hub[CONF_TIMEOUT] = minimum_scan_interval - 1
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
BASE_COMPONENT_SCHEMA = vol.Schema(
|
BASE_COMPONENT_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_NAME): cv.string,
|
vol.Required(CONF_NAME): cv.string,
|
||||||
|
@ -279,6 +316,7 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.All(
|
DOMAIN: vol.All(
|
||||||
cv.ensure_list,
|
cv.ensure_list,
|
||||||
|
control_scan_interval,
|
||||||
[
|
[
|
||||||
vol.Any(SERIAL_SCHEMA, ETHERNET_SCHEMA),
|
vol.Any(SERIAL_SCHEMA, ETHERNET_SCHEMA),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
"""Constants used in modbus integration."""
|
"""Constants used in modbus integration."""
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
|
from homeassistant.components.climate.const import DOMAIN as CLIMATE_DOMAIN
|
||||||
|
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
|
||||||
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_BINARY_SENSORS,
|
||||||
|
CONF_COVERS,
|
||||||
|
CONF_SENSORS,
|
||||||
|
CONF_SWITCHES,
|
||||||
|
)
|
||||||
|
|
||||||
# configuration names
|
# configuration names
|
||||||
CONF_BAUDRATE = "baudrate"
|
CONF_BAUDRATE = "baudrate"
|
||||||
CONF_BINARY_SENSOR = "binary_sensor"
|
|
||||||
CONF_BYTESIZE = "bytesize"
|
CONF_BYTESIZE = "bytesize"
|
||||||
CONF_CLIMATE = "climate"
|
|
||||||
CONF_CLIMATES = "climates"
|
CONF_CLIMATES = "climates"
|
||||||
CONF_COILS = "coils"
|
CONF_COILS = "coils"
|
||||||
CONF_COVER = "cover"
|
|
||||||
CONF_CURRENT_TEMP = "current_temp_register"
|
CONF_CURRENT_TEMP = "current_temp_register"
|
||||||
CONF_CURRENT_TEMP_REGISTER_TYPE = "current_temp_register_type"
|
CONF_CURRENT_TEMP_REGISTER_TYPE = "current_temp_register_type"
|
||||||
CONF_DATA_COUNT = "data_count"
|
CONF_DATA_COUNT = "data_count"
|
||||||
|
@ -24,7 +33,6 @@ CONF_REGISTERS = "registers"
|
||||||
CONF_REVERSE_ORDER = "reverse_order"
|
CONF_REVERSE_ORDER = "reverse_order"
|
||||||
CONF_PRECISION = "precision"
|
CONF_PRECISION = "precision"
|
||||||
CONF_SCALE = "scale"
|
CONF_SCALE = "scale"
|
||||||
CONF_SENSOR = "sensor"
|
|
||||||
CONF_STATE_CLOSED = "state_closed"
|
CONF_STATE_CLOSED = "state_closed"
|
||||||
CONF_STATE_CLOSING = "state_closing"
|
CONF_STATE_CLOSING = "state_closing"
|
||||||
CONF_STATE_OFF = "state_off"
|
CONF_STATE_OFF = "state_off"
|
||||||
|
@ -40,7 +48,6 @@ CONF_SWAP_BYTE = "byte"
|
||||||
CONF_SWAP_NONE = "none"
|
CONF_SWAP_NONE = "none"
|
||||||
CONF_SWAP_WORD = "word"
|
CONF_SWAP_WORD = "word"
|
||||||
CONF_SWAP_WORD_BYTE = "word_byte"
|
CONF_SWAP_WORD_BYTE = "word_byte"
|
||||||
CONF_SWITCH = "switch"
|
|
||||||
CONF_TARGET_TEMP = "target_temp_register"
|
CONF_TARGET_TEMP = "target_temp_register"
|
||||||
CONF_VERIFY = "verify"
|
CONF_VERIFY = "verify"
|
||||||
CONF_VERIFY_REGISTER = "verify_register"
|
CONF_VERIFY_REGISTER = "verify_register"
|
||||||
|
@ -74,6 +81,7 @@ SERVICE_WRITE_REGISTER = "write_register"
|
||||||
|
|
||||||
# integration names
|
# integration names
|
||||||
DEFAULT_HUB = "modbus_hub"
|
DEFAULT_HUB = "modbus_hub"
|
||||||
|
MINIMUM_SCAN_INTERVAL = 5 # seconds
|
||||||
DEFAULT_SCAN_INTERVAL = 15 # seconds
|
DEFAULT_SCAN_INTERVAL = 15 # seconds
|
||||||
DEFAULT_SLAVE = 1
|
DEFAULT_SLAVE = 1
|
||||||
DEFAULT_STRUCTURE_PREFIX = ">f"
|
DEFAULT_STRUCTURE_PREFIX = ">f"
|
||||||
|
@ -84,3 +92,11 @@ DEFAULT_STRUCT_FORMAT = {
|
||||||
}
|
}
|
||||||
DEFAULT_TEMP_UNIT = "C"
|
DEFAULT_TEMP_UNIT = "C"
|
||||||
MODBUS_DOMAIN = "modbus"
|
MODBUS_DOMAIN = "modbus"
|
||||||
|
|
||||||
|
PLATFORMS = (
|
||||||
|
(CLIMATE_DOMAIN, CONF_CLIMATES),
|
||||||
|
(COVER_DOMAIN, CONF_COVERS),
|
||||||
|
(BINARY_SENSOR_DOMAIN, CONF_BINARY_SENSORS),
|
||||||
|
(SENSOR_DOMAIN, CONF_SENSORS),
|
||||||
|
(SWITCH_DOMAIN, CONF_SWITCHES),
|
||||||
|
)
|
||||||
|
|
|
@ -8,15 +8,11 @@ from pymodbus.exceptions import ModbusException
|
||||||
from pymodbus.transaction import ModbusRtuFramer
|
from pymodbus.transaction import ModbusRtuFramer
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_BINARY_SENSORS,
|
|
||||||
CONF_COVERS,
|
|
||||||
CONF_DELAY,
|
CONF_DELAY,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_METHOD,
|
CONF_METHOD,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_SENSORS,
|
|
||||||
CONF_SWITCHES,
|
|
||||||
CONF_TIMEOUT,
|
CONF_TIMEOUT,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
|
@ -31,17 +27,12 @@ from .const import (
|
||||||
ATTR_UNIT,
|
ATTR_UNIT,
|
||||||
ATTR_VALUE,
|
ATTR_VALUE,
|
||||||
CONF_BAUDRATE,
|
CONF_BAUDRATE,
|
||||||
CONF_BINARY_SENSOR,
|
|
||||||
CONF_BYTESIZE,
|
CONF_BYTESIZE,
|
||||||
CONF_CLIMATE,
|
|
||||||
CONF_CLIMATES,
|
|
||||||
CONF_COVER,
|
|
||||||
CONF_PARITY,
|
CONF_PARITY,
|
||||||
CONF_SENSOR,
|
|
||||||
CONF_STOPBITS,
|
CONF_STOPBITS,
|
||||||
CONF_SWITCH,
|
|
||||||
DEFAULT_HUB,
|
DEFAULT_HUB,
|
||||||
MODBUS_DOMAIN as DOMAIN,
|
MODBUS_DOMAIN as DOMAIN,
|
||||||
|
PLATFORMS,
|
||||||
SERVICE_WRITE_COIL,
|
SERVICE_WRITE_COIL,
|
||||||
SERVICE_WRITE_REGISTER,
|
SERVICE_WRITE_REGISTER,
|
||||||
)
|
)
|
||||||
|
@ -63,13 +54,7 @@ def modbus_setup(
|
||||||
hub_collect[conf_hub[CONF_NAME]].setup(hass)
|
hub_collect[conf_hub[CONF_NAME]].setup(hass)
|
||||||
|
|
||||||
# load platforms
|
# load platforms
|
||||||
for component, conf_key in (
|
for component, conf_key in PLATFORMS:
|
||||||
(CONF_CLIMATE, CONF_CLIMATES),
|
|
||||||
(CONF_COVER, CONF_COVERS),
|
|
||||||
(CONF_BINARY_SENSOR, CONF_BINARY_SENSORS),
|
|
||||||
(CONF_SENSOR, CONF_SENSORS),
|
|
||||||
(CONF_SWITCH, CONF_SWITCHES),
|
|
||||||
):
|
|
||||||
if conf_key in conf_hub:
|
if conf_key in conf_hub:
|
||||||
load_platform(hass, component, DOMAIN, conf_hub, config)
|
load_platform(hass, component, DOMAIN, conf_hub, config)
|
||||||
|
|
||||||
|
@ -140,8 +125,7 @@ class ModbusHub:
|
||||||
self._config_port = client_config[CONF_PORT]
|
self._config_port = client_config[CONF_PORT]
|
||||||
self._config_timeout = client_config[CONF_TIMEOUT]
|
self._config_timeout = client_config[CONF_TIMEOUT]
|
||||||
self._config_delay = client_config[CONF_DELAY]
|
self._config_delay = client_config[CONF_DELAY]
|
||||||
|
Defaults.Timeout = client_config[CONF_TIMEOUT]
|
||||||
Defaults.Timeout = 10
|
|
||||||
if self._config_type == "serial":
|
if self._config_type == "serial":
|
||||||
# serial configuration
|
# serial configuration
|
||||||
self._config_method = client_config[CONF_METHOD]
|
self._config_method = client_config[CONF_METHOD]
|
||||||
|
|
|
@ -343,11 +343,14 @@ async def _read_helper(hass, do_group, do_type, do_return, do_exception, mock_py
|
||||||
CONF_HOST: "modbusTestHost",
|
CONF_HOST: "modbusTestHost",
|
||||||
CONF_PORT: 5501,
|
CONF_PORT: 5501,
|
||||||
CONF_NAME: TEST_MODBUS_NAME,
|
CONF_NAME: TEST_MODBUS_NAME,
|
||||||
do_group: {
|
do_group: [
|
||||||
CONF_INPUT_TYPE: do_type,
|
{
|
||||||
CONF_NAME: TEST_SENSOR_NAME,
|
CONF_INPUT_TYPE: do_type,
|
||||||
CONF_ADDRESS: 51,
|
CONF_NAME: TEST_SENSOR_NAME,
|
||||||
},
|
CONF_ADDRESS: 51,
|
||||||
|
CONF_SCAN_INTERVAL: 1,
|
||||||
|
}
|
||||||
|
],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue