diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index 9f851b4a235..8936ffc32ac 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -114,11 +114,7 @@ from .const import ( MODBUS_DOMAIN as DOMAIN, ) from .modbus import async_modbus_setup -from .validators import ( - number_validator, - scan_interval_validator, - sensor_schema_validator, -) +from .validators import number_validator, scan_interval_validator, struct_validator _LOGGER = logging.getLogger(__name__) @@ -145,7 +141,7 @@ BASE_STRUCT_SCHEMA = BASE_COMPONENT_SCHEMA.extend( CALL_TYPE_REGISTER_INPUT, ] ), - vol.Optional(CONF_COUNT, default=1): cv.positive_int, + vol.Optional(CONF_COUNT): cv.positive_int, vol.Optional(CONF_DATA_TYPE, default=DATA_TYPE_INT): vol.In( [ DATA_TYPE_INT16, @@ -289,12 +285,12 @@ MODBUS_SCHEMA = vol.Schema( cv.ensure_list, [BINARY_SENSOR_SCHEMA] ), vol.Optional(CONF_CLIMATES): vol.All( - cv.ensure_list, [vol.All(CLIMATE_SCHEMA, sensor_schema_validator)] + cv.ensure_list, [vol.All(CLIMATE_SCHEMA, struct_validator)] ), vol.Optional(CONF_COVERS): vol.All(cv.ensure_list, [COVERS_SCHEMA]), vol.Optional(CONF_LIGHTS): vol.All(cv.ensure_list, [LIGHT_SCHEMA]), vol.Optional(CONF_SENSORS): vol.All( - cv.ensure_list, [vol.All(SENSOR_SCHEMA, sensor_schema_validator)] + cv.ensure_list, [vol.All(SENSOR_SCHEMA, struct_validator)] ), vol.Optional(CONF_SWITCHES): vol.All(cv.ensure_list, [SWITCH_SCHEMA]), vol.Optional(CONF_FANS): vol.All(cv.ensure_list, [FAN_SCHEMA]), diff --git a/homeassistant/components/modbus/const.py b/homeassistant/components/modbus/const.py index 62319bcde85..49b7683435e 100644 --- a/homeassistant/components/modbus/const.py +++ b/homeassistant/components/modbus/const.py @@ -111,16 +111,16 @@ DEFAULT_SCAN_INTERVAL = 15 # seconds DEFAULT_SLAVE = 1 DEFAULT_STRUCTURE_PREFIX = ">f" DEFAULT_STRUCT_FORMAT = { - DATA_TYPE_INT16: "h", - DATA_TYPE_INT32: "i", - DATA_TYPE_INT64: "q", - DATA_TYPE_UINT16: "H", - DATA_TYPE_UINT32: "I", - DATA_TYPE_UINT64: "Q", - DATA_TYPE_FLOAT16: "e", - DATA_TYPE_FLOAT32: "f", - DATA_TYPE_FLOAT64: "d", - DATA_TYPE_STRING: "s", + DATA_TYPE_INT16: ["h", 1], + DATA_TYPE_INT32: ["i", 2], + DATA_TYPE_INT64: ["q", 4], + DATA_TYPE_UINT16: ["H", 1], + DATA_TYPE_UINT32: ["I", 2], + DATA_TYPE_UINT64: ["Q", 4], + DATA_TYPE_FLOAT16: ["e", 1], + DATA_TYPE_FLOAT32: ["f", 2], + DATA_TYPE_FLOAT64: ["d", 4], + DATA_TYPE_STRING: ["s", 1], } DEFAULT_TEMP_UNIT = "C" MODBUS_DOMAIN = "modbus" diff --git a/homeassistant/components/modbus/validators.py b/homeassistant/components/modbus/validators.py index e2777547ce4..9d72b611adc 100644 --- a/homeassistant/components/modbus/validators.py +++ b/homeassistant/components/modbus/validators.py @@ -40,7 +40,7 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -old_data_types = { +OLD_DATA_TYPES = { DATA_TYPE_INT: { 1: DATA_TYPE_INT16, 2: DATA_TYPE_INT32, @@ -59,39 +59,41 @@ old_data_types = { } -def sensor_schema_validator(config): +def struct_validator(config): """Sensor schema validator.""" data_type = config[CONF_DATA_TYPE] - count = config[CONF_COUNT] + count = config.get(CONF_COUNT, 1) name = config[CONF_NAME] structure = config.get(CONF_STRUCTURE) swap_type = config.get(CONF_SWAP) if data_type in [DATA_TYPE_INT, DATA_TYPE_UINT, DATA_TYPE_FLOAT]: - error = f"{name} {name} with {data_type} is not valid, trying to convert" + error = f"{name} with {data_type} is not valid, trying to convert" _LOGGER.warning(error) try: - data_type = old_data_types[data_type][count] + data_type = OLD_DATA_TYPES[data_type][config.get(CONF_COUNT, 1)] except KeyError as exp: - raise vol.Invalid("cannot convert automatically") from exp - + error = f"{name} cannot convert automatically {data_type}" + raise vol.Invalid(error) from exp if config[CONF_DATA_TYPE] != DATA_TYPE_CUSTOM: - try: - structure = f">{DEFAULT_STRUCT_FORMAT[data_type]}" - except KeyError as exp: - raise vol.Invalid(f"Modbus error {data_type} unknown in {name}") from exp + if structure: + error = f"{name} structure: cannot be mixed with {data_type}" + raise vol.Invalid(error) + structure = f">{DEFAULT_STRUCT_FORMAT[data_type][0]}" + if CONF_COUNT not in config: + config[CONF_COUNT] = DEFAULT_STRUCT_FORMAT[data_type][1] else: if not structure: - raise vol.Invalid( - f"Error in sensor {config[CONF_NAME]}. The `{CONF_STRUCTURE}` field can not be empty " - f"if the parameter `{CONF_DATA_TYPE}` is set to the `{DATA_TYPE_CUSTOM}`" + error = ( + f"Error in sensor {name}. The `{CONF_STRUCTURE}` field can not be empty" ) - + raise vol.Invalid(error) try: size = struct.calcsize(structure) except struct.error as err: raise vol.Invalid(f"Error in {name} structure: {str(err)}") from err + count = config.get(CONF_COUNT, 1) bytecount = count * 2 if bytecount != size: raise vol.Invalid( diff --git a/tests/components/modbus/test_init.py b/tests/components/modbus/test_init.py index ec68b98efa0..8b8d063bf02 100644 --- a/tests/components/modbus/test_init.py +++ b/tests/components/modbus/test_init.py @@ -55,7 +55,7 @@ from homeassistant.components.modbus.const import ( ) from homeassistant.components.modbus.validators import ( number_validator, - sensor_schema_validator, + struct_validator, ) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ( @@ -144,12 +144,12 @@ async def test_number_validator(): }, ], ) -async def test_ok_sensor_schema_validator(do_config): +async def test_ok_struct_validator(do_config): """Test struct validator.""" try: - sensor_schema_validator(do_config) + struct_validator(do_config) except vol.Invalid: - pytest.fail("Sensor_schema_validator unexpected exception") + pytest.fail("struct_validator unexpected exception") @pytest.mark.parametrize( @@ -186,13 +186,13 @@ async def test_ok_sensor_schema_validator(do_config): }, ], ) -async def test_exception_sensor_schema_validator(do_config): +async def test_exception_struct_validator(do_config): """Test struct validator.""" try: - sensor_schema_validator(do_config) + struct_validator(do_config) except vol.Invalid: return - pytest.fail("Sensor_schema_validator missing exception") + pytest.fail("struct_validator missing exception") @pytest.mark.parametrize( diff --git a/tests/components/modbus/test_sensor.py b/tests/components/modbus/test_sensor.py index 9b0c868f8cb..f01a3ef9da5 100644 --- a/tests/components/modbus/test_sensor.py +++ b/tests/components/modbus/test_sensor.py @@ -174,7 +174,7 @@ async def test_config_sensor(hass, mock_modbus): CONF_SWAP: CONF_SWAP_NONE, CONF_STRUCTURE: "", }, - "Error in sensor test_sensor. The `structure` field can not be empty if the parameter `data_type` is set to the `custom`", + "Error in sensor test_sensor. The `structure` field can not be empty", ), ( {