diff --git a/.coveragerc b/.coveragerc index c25b0f45c7e..eb137602a75 100644 --- a/.coveragerc +++ b/.coveragerc @@ -538,7 +538,9 @@ omit = homeassistant/components/mjpeg/camera.py homeassistant/components/mobile_app/* homeassistant/components/mochad/* - homeassistant/components/modbus/* + homeassistant/components/modbus/climate.py + homeassistant/components/modbus/cover.py + homeassistant/components/modbus/switch.py homeassistant/components/modem_callerid/sensor.py homeassistant/components/mpchc/media_player.py homeassistant/components/mpd/media_player.py diff --git a/tests/components/modbus/conftest.py b/tests/components/modbus/conftest.py index 885aa5fc235..66f4d542525 100644 --- a/tests/components/modbus/conftest.py +++ b/tests/components/modbus/conftest.py @@ -6,14 +6,13 @@ from unittest import mock import pytest from homeassistant.components.modbus.const import ( + CALL_TYPE_COIL, + CALL_TYPE_DISCRETE, CALL_TYPE_REGISTER_INPUT, - CONF_REGISTER, - CONF_REGISTER_TYPE, - CONF_REGISTERS, DEFAULT_HUB, MODBUS_DOMAIN as DOMAIN, ) -from homeassistant.const import CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL +from homeassistant.const import CONF_PLATFORM, CONF_SCAN_INTERVAL from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -38,31 +37,40 @@ class ReadResult: def __init__(self, register_words): """Init.""" self.registers = register_words + self.bits = register_words -async def run_test( - hass, use_mock_hub, register_config, entity_domain, register_words, expected +async def run_base_test( + sensor_name, + hass, + use_mock_hub, + data_array, + register_type, + entity_domain, + register_words, + expected, ): - """Run test for given config and check that sensor outputs expected result.""" + """Run test for given config.""" # Full sensor configuration - sensor_name = "modbus_test_sensor" scan_interval = 5 config = { entity_domain: { CONF_PLATFORM: "modbus", CONF_SCAN_INTERVAL: scan_interval, - CONF_REGISTERS: [ - dict(**{CONF_NAME: sensor_name, CONF_REGISTER: 1234}, **register_config) - ], + **data_array, } } # Setup inputs for the sensor read_result = ReadResult(register_words) - if register_config.get(CONF_REGISTER_TYPE) == CALL_TYPE_REGISTER_INPUT: + if register_type == CALL_TYPE_COIL: + use_mock_hub.read_coils.return_value = read_result + elif register_type == CALL_TYPE_DISCRETE: + use_mock_hub.read_discrete_inputs.return_value = read_result + elif register_type == CALL_TYPE_REGISTER_INPUT: use_mock_hub.read_input_registers.return_value = read_result - else: + else: # CALL_TYPE_REGISTER_HOLDING use_mock_hub.read_holding_registers.return_value = read_result # Initialize sensor @@ -76,8 +84,3 @@ async def run_test( 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 - entity_id = f"{entity_domain}.{sensor_name}" - 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 new file mode 100644 index 00000000000..ff64ad8723a --- /dev/null +++ b/tests/components/modbus/test_modbus_binary_sensor.py @@ -0,0 +1,98 @@ +"""The tests for the Modbus sensor component.""" +import logging + +from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.modbus.const import ( + CALL_TYPE_COIL, + CALL_TYPE_DISCRETE, + CONF_ADDRESS, + CONF_INPUT_TYPE, + CONF_INPUTS, +) +from homeassistant.const import CONF_NAME, STATE_OFF, STATE_ON + +from .conftest import run_base_test + +_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( + sensor_name, + hass, + use_mock_hub, + data_array, + register_config.get(CONF_INPUT_TYPE), + entity_domain, + value, + expected, + ) + + # 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.""" + register_config = { + CONF_INPUT_TYPE: CALL_TYPE_COIL, + } + await run_sensor_test( + hass, + mock_hub, + register_config, + [0xFF], + STATE_ON, + ) + + +async def test_coil_false(hass, mock_hub): + """Test conversion of single word register.""" + register_config = { + CONF_INPUT_TYPE: CALL_TYPE_COIL, + } + await run_sensor_test( + hass, + mock_hub, + register_config, + [0x00], + STATE_OFF, + ) + + +async def test_discrete_true(hass, mock_hub): + """Test conversion of single word register.""" + register_config = { + CONF_INPUT_TYPE: CALL_TYPE_DISCRETE, + } + await run_sensor_test( + hass, + mock_hub, + register_config, + [0xFF], + expected="on", + ) + + +async def test_discrete_false(hass, mock_hub): + """Test conversion of single word register.""" + register_config = { + CONF_INPUT_TYPE: CALL_TYPE_DISCRETE, + } + await run_sensor_test( + hass, + mock_hub, + register_config, + [0x00], + expected="off", + ) diff --git a/tests/components/modbus/test_modbus_sensor.py b/tests/components/modbus/test_modbus_sensor.py index ab4d745dc50..5ade2f197dd 100644 --- a/tests/components/modbus/test_modbus_sensor.py +++ b/tests/components/modbus/test_modbus_sensor.py @@ -8,7 +8,9 @@ from homeassistant.components.modbus.const import ( CONF_DATA_TYPE, CONF_OFFSET, CONF_PRECISION, + CONF_REGISTER, CONF_REGISTER_TYPE, + CONF_REGISTERS, CONF_REVERSE_ORDER, CONF_SCALE, DATA_TYPE_FLOAT, @@ -17,12 +19,42 @@ from homeassistant.components.modbus.const import ( DATA_TYPE_UINT, ) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import CONF_NAME -from .conftest import run_test +from .conftest import run_base_test _LOGGER = logging.getLogger(__name__) +async def run_sensor_test( + hass, use_mock_hub, register_config, register_words, expected +): + """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( + sensor_name, + hass, + use_mock_hub, + data_array, + register_config.get(CONF_REGISTER_TYPE), + entity_domain, + register_words, + expected, + ) + + # 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 = { @@ -32,11 +64,10 @@ async def test_simple_word_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0], expected="0", ) @@ -45,11 +76,10 @@ async def test_simple_word_register(hass, mock_hub): async def test_optional_conf_keys(hass, mock_hub): """Test handling of optional configuration keys.""" register_config = {} - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x8000], expected="-32768", ) @@ -64,11 +94,10 @@ async def test_offset(hass, mock_hub): CONF_OFFSET: 13, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[7], expected="20", ) @@ -83,11 +112,10 @@ async def test_scale_and_offset(hass, mock_hub): CONF_OFFSET: 13, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[7], expected="34", ) @@ -102,11 +130,10 @@ async def test_ints_can_have_precision(hass, mock_hub): CONF_OFFSET: 13, CONF_PRECISION: 4, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[7], expected="34.0000", ) @@ -121,11 +148,10 @@ async def test_floats_get_rounded_correctly(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[1], expected="2", ) @@ -140,11 +166,10 @@ async def test_parameters_as_strings(hass, mock_hub): CONF_OFFSET: "5", CONF_PRECISION: "1", } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[9], expected="18.5", ) @@ -159,11 +184,10 @@ async def test_floating_point_scale(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 2, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[1], expected="2.40", ) @@ -178,11 +202,10 @@ async def test_floating_point_offset(hass, mock_hub): CONF_OFFSET: -10.3, CONF_PRECISION: 1, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[2], expected="-8.3", ) @@ -197,11 +220,10 @@ async def test_signed_two_word_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected="-1985229329", ) @@ -216,11 +238,10 @@ async def test_unsigned_two_word_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0x89ABCDEF), ) @@ -233,11 +254,10 @@ async def test_reversed(hass, mock_hub): CONF_DATA_TYPE: DATA_TYPE_UINT, CONF_REVERSE_ORDER: True, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0xCDEF89AB), ) @@ -252,11 +272,10 @@ async def test_four_word_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF, 0x0123, 0x4567], expected="9920249030613615975", ) @@ -271,11 +290,10 @@ async def test_four_word_register_precision_is_intact_with_int_params(hass, mock CONF_OFFSET: 3, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF], expected="163971058432973793", ) @@ -290,11 +308,10 @@ async def test_four_word_register_precision_is_lost_with_float_params(hass, mock CONF_OFFSET: 3.0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x0123, 0x4567, 0x89AB, 0xCDEF], expected="163971058432973792", ) @@ -310,11 +327,10 @@ async def test_two_word_input_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0x89ABCDEF), ) @@ -330,11 +346,10 @@ async def test_two_word_holding_register(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x89AB, 0xCDEF], expected=str(0x89ABCDEF), ) @@ -350,11 +365,10 @@ async def test_float_data_type(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 5, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[16286, 1617], expected="1.23457", ) @@ -370,11 +384,10 @@ async def test_string_data_type(hass, mock_hub): CONF_OFFSET: 0, CONF_PRECISION: 0, } - await run_test( + await run_sensor_test( hass, mock_hub, register_config, - SENSOR_DOMAIN, register_words=[0x3037, 0x2D30, 0x352D, 0x3230, 0x3230, 0x2031, 0x343A, 0x3335], expected="07-05-2020 14:35", )