diff --git a/tests/components/modbus/conftest.py b/tests/components/modbus/conftest.py index 66f4d542525dc45fdcf0686d3660a131c3b796d6..36495acc9c5d11db1a95f92be4e8be27aa37b30a 100644 --- a/tests/components/modbus/conftest.py +++ b/tests/components/modbus/conftest.py @@ -1,5 +1,4 @@ """The tests for the Modbus sensor component.""" -from datetime import timedelta import logging from unittest import mock @@ -40,20 +39,17 @@ class ReadResult: self.bits = register_words -async def run_base_test( +async def setup_base_test( sensor_name, hass, use_mock_hub, data_array, - register_type, entity_domain, - register_words, - expected, + scan_interval, ): - """Run test for given config.""" + """Run setup device for given config.""" # Full sensor configuration - scan_interval = 5 config = { entity_domain: { CONF_PLATFORM: "modbus", @@ -62,6 +58,28 @@ async def run_base_test( } } + # Initialize sensor + now = dt_util.utcnow() + with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): + assert await async_setup_component(hass, entity_domain, config) + await hass.async_block_till_done() + + entity_id = f"{entity_domain}.{sensor_name}" + device = hass.states.get(entity_id) + return entity_id, now, device + + +async def run_base_read_test( + entity_id, + hass, + use_mock_hub, + register_type, + register_words, + expected, + now, +): + """Run test for given config.""" + # Setup inputs for the sensor read_result = ReadResult(register_words) if register_type == CALL_TYPE_COIL: @@ -73,14 +91,11 @@ async def run_base_test( else: # CALL_TYPE_REGISTER_HOLDING use_mock_hub.read_holding_registers.return_value = read_result - # Initialize sensor - now = dt_util.utcnow() - with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): - assert await async_setup_component(hass, entity_domain, config) - await hass.async_block_till_done() - # Trigger update call with time_changed event - now += timedelta(seconds=scan_interval + 1) with mock.patch("homeassistant.helpers.event.dt_util.utcnow", return_value=now): async_fire_time_changed(hass, now) await hass.async_block_till_done() + + # Check state + state = hass.states.get(entity_id).state + assert state == expected diff --git a/tests/components/modbus/test_modbus_binary_sensor.py b/tests/components/modbus/test_modbus_binary_sensor.py index ff64ad8723ad59c2b71510e760df066635bfa0d0..63513872e79fe5fe6033b11f3ecba385791a0518 100644 --- a/tests/components/modbus/test_modbus_binary_sensor.py +++ b/tests/components/modbus/test_modbus_binary_sensor.py @@ -1,4 +1,5 @@ """The tests for the Modbus sensor component.""" +from datetime import timedelta import logging from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN @@ -11,7 +12,7 @@ from homeassistant.components.modbus.const import ( ) from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON -from .conftest import run_base_test +from .conftest import run_base_read_test, setup_base_test _LOGGER = logging.getLogger(__name__) @@ -19,28 +20,29 @@ _LOGGER = logging.getLogger(__name__) async def run_sensor_test(hass, use_mock_hub, register_config, value, expected): """Run test for given config.""" sensor_name = "modbus_test_binary_sensor" - entity_domain = SENSOR_DOMAIN - data_array = { - CONF_INPUTS: [ - dict(**{CONF_NAME: sensor_name, CONF_ADDRESS: 1234}, **register_config) - ] - } - await run_base_test( + scan_interval = 5 + entity_id, now, device = await setup_base_test( sensor_name, hass, use_mock_hub, - data_array, + { + CONF_INPUTS: [ + dict(**{CONF_NAME: sensor_name, CONF_ADDRESS: 1234}, **register_config) + ] + }, + SENSOR_DOMAIN, + scan_interval, + ) + await run_base_read_test( + entity_id, + hass, + use_mock_hub, register_config.get(CONF_INPUT_TYPE), - entity_domain, value, expected, + now + timedelta(seconds=scan_interval + 1), ) - # Check state - entity_id = f"{entity_domain}.{sensor_name}" - state = hass.states.get(entity_id).state - assert state == expected - async def test_coil_true(hass, mock_hub): """Test conversion of single word register.""" diff --git a/tests/components/modbus/test_modbus_sensor.py b/tests/components/modbus/test_modbus_sensor.py index 5ade2f197ddb6d5d6377ea4e0de8bfdad76dcbb7..59d845ef053bf01464b2874f11c4f0af44744b4e 100644 --- a/tests/components/modbus/test_modbus_sensor.py +++ b/tests/components/modbus/test_modbus_sensor.py @@ -1,4 +1,5 @@ """The tests for the Modbus sensor component.""" +from datetime import timedelta import logging from homeassistant.components.modbus.const import ( @@ -21,7 +22,7 @@ from homeassistant.components.modbus.const import ( from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import CONF_NAME -from .conftest import run_base_test +from .conftest import run_base_read_test, setup_base_test _LOGGER = logging.getLogger(__name__) @@ -31,363 +32,344 @@ async def run_sensor_test( ): """Run test for sensor.""" sensor_name = "modbus_test_sensor" - entity_domain = SENSOR_DOMAIN - data_array = { - CONF_REGISTERS: [ - dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config) - ] - } - - await run_base_test( + scan_interval = 5 + entity_id, now, device = await setup_base_test( sensor_name, hass, use_mock_hub, - data_array, + { + CONF_REGISTERS: [ + dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config) + ] + }, + SENSOR_DOMAIN, + scan_interval, + ) + await run_base_read_test( + entity_id, + hass, + use_mock_hub, register_config.get(CONF_REGISTER_TYPE), - entity_domain, register_words, expected, + now + timedelta(seconds=scan_interval + 1), ) - # Check state - entity_id = f"{entity_domain}.{sensor_name}" - state = hass.states.get(entity_id).state - assert state == expected - async def test_simple_word_register(hass, mock_hub): """Test conversion of single word register.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0], - expected="0", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0], + "0", ) async def test_optional_conf_keys(hass, mock_hub): """Test handling of optional configuration keys.""" - register_config = {} await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x8000], - expected="-32768", + {}, + [0x8000], + "-32768", ) async def test_offset(hass, mock_hub): """Test offset calculation.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 1, - CONF_OFFSET: 13, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[7], - expected="20", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 1, + CONF_OFFSET: 13, + CONF_PRECISION: 0, + }, + [7], + "20", ) async def test_scale_and_offset(hass, mock_hub): """Test handling of scale and offset.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 3, - CONF_OFFSET: 13, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[7], - expected="34", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 3, + CONF_OFFSET: 13, + CONF_PRECISION: 0, + }, + [7], + "34", ) async def test_ints_can_have_precision(hass, mock_hub): """Test precision can be specified event if using integer values only.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 3, - CONF_OFFSET: 13, - CONF_PRECISION: 4, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[7], - expected="34.0000", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 3, + CONF_OFFSET: 13, + CONF_PRECISION: 4, + }, + [7], + "34.0000", ) async def test_floats_get_rounded_correctly(hass, mock_hub): """Test that floating point values get rounded correctly.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 1.5, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[1], - expected="2", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 1.5, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [1], + "2", ) async def test_parameters_as_strings(hass, mock_hub): """Test that scale, offset and precision can be given as strings.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: "1.5", - CONF_OFFSET: "5", - CONF_PRECISION: "1", - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[9], - expected="18.5", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: "1.5", + CONF_OFFSET: "5", + CONF_PRECISION: "1", + }, + [9], + "18.5", ) async def test_floating_point_scale(hass, mock_hub): """Test use of floating point scale.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 2.4, - CONF_OFFSET: 0, - CONF_PRECISION: 2, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[1], - expected="2.40", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 2.4, + CONF_OFFSET: 0, + CONF_PRECISION: 2, + }, + [1], + "2.40", ) async def test_floating_point_offset(hass, mock_hub): """Test use of floating point scale.""" - register_config = { - CONF_COUNT: 1, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 1, - CONF_OFFSET: -10.3, - CONF_PRECISION: 1, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[2], - expected="-8.3", + { + CONF_COUNT: 1, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 1, + CONF_OFFSET: -10.3, + CONF_PRECISION: 1, + }, + [2], + "-8.3", ) async def test_signed_two_word_register(hass, mock_hub): """Test reading of signed register with two words.""" - register_config = { - CONF_COUNT: 2, - CONF_DATA_TYPE: DATA_TYPE_INT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x89AB, 0xCDEF], - expected="-1985229329", + { + CONF_COUNT: 2, + CONF_DATA_TYPE: DATA_TYPE_INT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0x89AB, 0xCDEF], + "-1985229329", ) async def test_unsigned_two_word_register(hass, mock_hub): """Test reading of unsigned register with two words.""" - register_config = { - CONF_COUNT: 2, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x89AB, 0xCDEF], - expected=str(0x89ABCDEF), + { + CONF_COUNT: 2, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0x89AB, 0xCDEF], + str(0x89ABCDEF), ) async def test_reversed(hass, mock_hub): """Test handling of reversed register words.""" - register_config = { - CONF_COUNT: 2, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_REVERSE_ORDER: True, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x89AB, 0xCDEF], - expected=str(0xCDEF89AB), + { + CONF_COUNT: 2, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_REVERSE_ORDER: True, + }, + [0x89AB, 0xCDEF], + str(0xCDEF89AB), ) async def test_four_word_register(hass, mock_hub): """Test reading of 64-bit register.""" - register_config = { - CONF_COUNT: 4, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x89AB, 0xCDEF, 0x0123, 0x4567], - expected="9920249030613615975", + { + CONF_COUNT: 4, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0x89AB, 0xCDEF, 0x0123, 0x4567], + "9920249030613615975", ) async def test_four_word_register_precision_is_intact_with_int_params(hass, mock_hub): """Test that precision is not lost when doing integer arithmetic for 64-bit register.""" - register_config = { - CONF_COUNT: 4, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 2, - CONF_OFFSET: 3, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF], - expected="163971058432973793", + { + CONF_COUNT: 4, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 2, + CONF_OFFSET: 3, + CONF_PRECISION: 0, + }, + [0x0123, 0x4567, 0x89AB, 0xCDEF], + "163971058432973793", ) async def test_four_word_register_precision_is_lost_with_float_params(hass, mock_hub): """Test that precision is affected when floating point conversion is done.""" - register_config = { - CONF_COUNT: 4, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 2.0, - CONF_OFFSET: 3.0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF], - expected="163971058432973792", + { + CONF_COUNT: 4, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 2.0, + CONF_OFFSET: 3.0, + CONF_PRECISION: 0, + }, + [0x0123, 0x4567, 0x89AB, 0xCDEF], + "163971058432973792", ) async def test_two_word_input_register(hass, mock_hub): """Test reaging of input register.""" - register_config = { - CONF_COUNT: 2, - CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_INPUT, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x89AB, 0xCDEF], - expected=str(0x89ABCDEF), + { + CONF_COUNT: 2, + CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_INPUT, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0x89AB, 0xCDEF], + str(0x89ABCDEF), ) async def test_two_word_holding_register(hass, mock_hub): """Test reaging of holding register.""" - register_config = { - CONF_COUNT: 2, - CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_HOLDING, - CONF_DATA_TYPE: DATA_TYPE_UINT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x89AB, 0xCDEF], - expected=str(0x89ABCDEF), + { + CONF_COUNT: 2, + CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_HOLDING, + CONF_DATA_TYPE: DATA_TYPE_UINT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0x89AB, 0xCDEF], + str(0x89ABCDEF), ) async def test_float_data_type(hass, mock_hub): """Test floating point register data type.""" - register_config = { - CONF_COUNT: 2, - CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_HOLDING, - CONF_DATA_TYPE: DATA_TYPE_FLOAT, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 5, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[16286, 1617], - expected="1.23457", + { + CONF_COUNT: 2, + CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_HOLDING, + CONF_DATA_TYPE: DATA_TYPE_FLOAT, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 5, + }, + [16286, 1617], + "1.23457", ) async def test_string_data_type(hass, mock_hub): """Test byte string register data type.""" - register_config = { - CONF_COUNT: 8, - CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_HOLDING, - CONF_DATA_TYPE: DATA_TYPE_STRING, - CONF_SCALE: 1, - CONF_OFFSET: 0, - CONF_PRECISION: 0, - } await run_sensor_test( hass, mock_hub, - register_config, - register_words=[0x3037, 0x2D30, 0x352D, 0x3230, 0x3230, 0x2031, 0x343A, 0x3335], - expected="07-05-2020 14:35", + { + CONF_COUNT: 8, + CONF_REGISTER_TYPE: CALL_TYPE_REGISTER_HOLDING, + CONF_DATA_TYPE: DATA_TYPE_STRING, + CONF_SCALE: 1, + CONF_OFFSET: 0, + CONF_PRECISION: 0, + }, + [0x3037, 0x2D30, 0x352D, 0x3230, 0x3230, 0x2031, 0x343A, 0x3335], + "07-05-2020 14:35", ) diff --git a/tests/components/modbus/test_modbus_switch.py b/tests/components/modbus/test_modbus_switch.py new file mode 100644 index 0000000000000000000000000000000000000000..ac1d8bd696308ada38b5358303e528a858ae212f --- /dev/null +++ b/tests/components/modbus/test_modbus_switch.py @@ -0,0 +1,59 @@ +"""The tests for the Modbus switch component.""" +from datetime import timedelta +import logging + +from homeassistant.components.modbus.const import CALL_TYPE_COIL, CONF_COILS +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.const import CONF_NAME, CONF_SLAVE + +from .conftest import run_base_read_test, setup_base_test + +_LOGGER = logging.getLogger(__name__) + + +async def run_sensor_test(hass, use_mock_hub, value, expected): + """Run test for given config.""" + switch_name = "modbus_test_switch" + scan_interval = 5 + entity_id, now, device = await setup_base_test( + switch_name, + hass, + use_mock_hub, + { + CONF_COILS: [ + {CONF_NAME: switch_name, CALL_TYPE_COIL: 1234, CONF_SLAVE: 1}, + ] + }, + SWITCH_DOMAIN, + scan_interval, + ) + + await run_base_read_test( + entity_id, + hass, + use_mock_hub, + CALL_TYPE_COIL, + value, + expected, + now + timedelta(seconds=scan_interval + 1), + ) + + +async def test_read_coil_false(hass, mock_hub): + """Test reading of switch coil.""" + await run_sensor_test( + hass, + mock_hub, + [0x00], + expected="off", + ) + + +async def test_read_coil_true(hass, mock_hub): + """Test reading of switch coil.""" + await run_sensor_test( + hass, + mock_hub, + [0xFF], + expected="on", + )