def testUdpClientConnect(self):
        ''' Test the Udp client connection method'''
        with patch.object(socket, 'socket') as mock_method:
            mock_method.return_value = object()
            client = ModbusUdpClient()
            self.assertTrue(client.connect())

        with patch.object(socket, 'socket') as mock_method:
            mock_method.side_effect = socket.error()
            client = ModbusUdpClient()
            self.assertFalse(client.connect())
예제 #2
0
    def testUdpClientConnect(self):
        ''' Test the Udp client connection method'''
        with patch.object(socket, 'socket') as mock_method:
            mock_method.return_value = object()
            client = ModbusUdpClient()
            self.assertTrue(client.connect())

        with patch.object(socket, 'socket') as mock_method:
            mock_method.side_effect = socket.error()
            client = ModbusUdpClient()
            self.assertFalse(client.connect())
예제 #3
0
    def testUdpClientConnect(self):
        ''' Test the Udp client connection method'''
        with patch.object(socket, 'socket') as mock_method:
            class DummySocket(object):
                def settimeout(self, *a, **kwa):
                    pass
            mock_method.return_value = DummySocket()
            client = ModbusUdpClient()
            self.assertTrue(client.connect())

        with patch.object(socket, 'socket') as mock_method:
            mock_method.side_effect = socket.error()
            client = ModbusUdpClient()
            self.assertFalse(client.connect())
예제 #4
0
    def testUdpClientConnect(self):
        ''' Test the Udp client connection method'''
        with patch.object(socket, 'socket') as mock_method:
            class DummySocket(object):
                def settimeout(self, *a, **kwa):
                    pass
            mock_method.return_value = DummySocket()
            client = ModbusUdpClient()
            self.assertTrue(client.connect())

        with patch.object(socket, 'socket') as mock_method:
            mock_method.side_effect = socket.error()
            client = ModbusUdpClient()
            self.assertFalse(client.connect())
예제 #5
0
def do_connect_udp(host_port: str):
    """
    Connect to a Modbus UDP device

    :PARAM: host_port: <IP/HOSTNAME>[:<PORT>]
    """
    assert (
        ctmodbus.session == None
    ), "Session already open.  Close first."  # ToDo assert session type
    host, port = common.parse_ip_port(host_port)
    s = ModbusUdpClient(host, port, timeout=3)
    assert s.connect(), f"Could not connect to {host}:{port}"
    ctmodbus.session = s
    date, time = str(datetime.today()).split()
    return (ctmodbus.output_text +
            f"UDP session OPENED with {host}:{port} at {date} {time}\n")
예제 #6
0
    def testBasicSyncUdpClient(self):
        ''' Test the basic methods for the udp sync client'''

        # receive/send
        client = ModbusUdpClient()
        client.socket = mockSocket()
        self.assertEqual(0, client._send(None))
        self.assertEqual(1, client._send('\x00'))
        self.assertEqual('\x00', client._recv(1))

        # connect/disconnect
        self.assertTrue(client.connect())
        client.close()

        # already closed socket
        client.socket = False
        client.close()

        self.assertEqual("127.0.0.1:502", str(client))
    def testBasicSyncUdpClient(self):
        ''' Test the basic methods for the udp sync client'''

        # receive/send
        client = ModbusUdpClient()
        client.socket = mockSocket()
        self.assertEqual(0, client._send(None))
        self.assertEqual(1, client._send('\x00'))
        self.assertEqual('\x00', client._recv(1))

        # connect/disconnect
        self.assertTrue(client.connect())
        client.close()

        # already closed socket
        client.socket = False
        client.close()

        self.assertEqual("127.0.0.1:502", str(client))
예제 #8
0
파일: __init__.py 프로젝트: aneip/core-1
class ModbusHub:
    """Thread safe wrapper class for pymodbus."""
    def __init__(self, client_config):
        """Initialize the Modbus hub."""

        # generic configuration
        self._client = None
        self._lock = threading.Lock()
        self._config_name = client_config[CONF_NAME]
        self._config_type = client_config[CONF_TYPE]
        self._config_port = client_config[CONF_PORT]
        self._config_timeout = client_config[CONF_TIMEOUT]
        self._config_delay = 0

        if self._config_type == "serial":
            # serial configuration
            self._config_method = client_config[CONF_METHOD]
            self._config_baudrate = client_config[CONF_BAUDRATE]
            self._config_stopbits = client_config[CONF_STOPBITS]
            self._config_bytesize = client_config[CONF_BYTESIZE]
            self._config_parity = client_config[CONF_PARITY]
        else:
            # network configuration
            self._config_host = client_config[CONF_HOST]
            self._config_delay = client_config[CONF_DELAY]
            if self._config_delay > 0:
                _LOGGER.warning(
                    "Parameter delay is accepted but not used in this version")

    @property
    def name(self):
        """Return the name of this hub."""
        return self._config_name

    def setup(self):
        """Set up pymodbus client."""
        if self._config_type == "serial":
            self._client = ModbusSerialClient(
                method=self._config_method,
                port=self._config_port,
                baudrate=self._config_baudrate,
                stopbits=self._config_stopbits,
                bytesize=self._config_bytesize,
                parity=self._config_parity,
                timeout=self._config_timeout,
            )
        elif self._config_type == "rtuovertcp":
            self._client = ModbusTcpClient(
                host=self._config_host,
                port=self._config_port,
                framer=ModbusRtuFramer,
                timeout=self._config_timeout,
            )
        elif self._config_type == "tcp":
            self._client = ModbusTcpClient(
                host=self._config_host,
                port=self._config_port,
                timeout=self._config_timeout,
            )
        elif self._config_type == "udp":
            self._client = ModbusUdpClient(
                host=self._config_host,
                port=self._config_port,
                timeout=self._config_timeout,
            )
        else:
            assert False

        # Connect device
        self.connect()

    def close(self):
        """Disconnect client."""
        with self._lock:
            self._client.close()

    def connect(self):
        """Connect client."""
        with self._lock:
            self._client.connect()

    def read_coils(self, unit, address, count):
        """Read coils."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            return self._client.read_coils(address, count, **kwargs)

    def read_discrete_inputs(self, unit, address, count):
        """Read discrete inputs."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            return self._client.read_discrete_inputs(address, count, **kwargs)

    def read_input_registers(self, unit, address, count):
        """Read input registers."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            return self._client.read_input_registers(address, count, **kwargs)

    def read_holding_registers(self, unit, address, count):
        """Read holding registers."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            return self._client.read_holding_registers(address, count,
                                                       **kwargs)

    def write_coil(self, unit, address, value):
        """Write coil."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            self._client.write_coil(address, value, **kwargs)

    def write_register(self, unit, address, value):
        """Write register."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            self._client.write_register(address, value, **kwargs)

    def write_registers(self, unit, address, values):
        """Write registers."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            self._client.write_registers(address, values, **kwargs)
class ModbusHub:
    def __init__(self, protocol, host, port, slave):
        self._lock = threading.Lock()
        self._slave = slave
        if (protocol == "rtuovertcp"):
            self._client = ModbusTcpClient(host=host,
                                           port=port,
                                           framer=ModbusRtuFramer,
                                           timeout=2,
                                           retry_on_empty=True,
                                           retry_on_invalid=False)
        elif (protocol == "rtuoverudp"):
            self._client = ModbusUdpClient(host=host,
                                           port=port,
                                           framer=ModbusRtuFramer,
                                           timeout=2,
                                           retry_on_empty=False,
                                           retry_on_invalid=False)

    def connect(self):
        with self._lock:
            self._client.connect()

    def close(self):
        with self._lock:
            self._client.close()

    def read_holding_register(self):
        pass

    def read_input_registers(self, address, count):
        with self._lock:
            kwargs = {"unit": self._slave}
            return self._client.read_input_registers(address, count, **kwargs)

    def reset_energy(self):
        with self._lock:
            kwargs = {"unit": self._slave}
            request = ModbusResetEnergyRequest(**kwargs)
            self._client.execute(request)

    def info_gather(self):
        data = {}
        try:
            result = self.read_input_registers(0, 9)
            if result is not None and type(result) is not ModbusIOException \
                    and result.registers is not None and len(result.registers) == 9:
                data[DEVICE_CLASS_VOLTAGE] = result.registers[0] / 10
                data[DEVICE_CLASS_CURRENT] = (
                    (result.registers[2] << 16) + result.registers[1]) / 1000
                data[DEVICE_CLASS_POWER] = (
                    (result.registers[4] << 16) + result.registers[3]) / 10
                data[DEVICE_CLASS_ENERGY] = (
                    (result.registers[6] << 16) + result.registers[5]) / 1000
                data[DEVICE_CLASS_FREQUENCY] = result.registers[7] / 10
                data[DEVICE_CLASS_POWER_FACTOR] = result.registers[8] / 100
            else:
                _LOGGER.debug(f"Error in gathering, timed out")
        except Exception as e:
            _LOGGER.error(f"Error in gathering, {e}")
        return data
예제 #10
0
class ModbusHub:
    """Thread safe wrapper class for pymodbus."""
    def __init__(self, hass, client_config):
        """Initialize the Modbus hub."""

        # generic configuration
        self._client = None
        self._async_cancel_listener = None
        self._in_error = False
        self._lock = asyncio.Lock()
        self.hass = hass
        self._config_name = client_config[CONF_NAME]
        self._config_type = client_config[CONF_TYPE]
        self._config_port = client_config[CONF_PORT]
        self._config_timeout = client_config[CONF_TIMEOUT]
        self._config_delay = client_config[CONF_DELAY]
        self._config_reset_socket = client_config[CONF_CLOSE_COMM_ON_ERROR]
        Defaults.Timeout = client_config[CONF_TIMEOUT]
        if self._config_type == "serial":
            # serial configuration
            self._config_method = client_config[CONF_METHOD]
            self._config_baudrate = client_config[CONF_BAUDRATE]
            self._config_stopbits = client_config[CONF_STOPBITS]
            self._config_bytesize = client_config[CONF_BYTESIZE]
            self._config_parity = client_config[CONF_PARITY]
        else:
            # network configuration
            self._config_host = client_config[CONF_HOST]

        self._call_type = {
            CALL_TYPE_COIL: {
                ENTRY_ATTR: "bits",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_DISCRETE: {
                ENTRY_ATTR: "bits",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_REGISTER_HOLDING: {
                ENTRY_ATTR: "registers",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_REGISTER_INPUT: {
                ENTRY_ATTR: "registers",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_WRITE_COIL: {
                ENTRY_ATTR: "value",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_WRITE_COILS: {
                ENTRY_ATTR: "count",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_WRITE_REGISTER: {
                ENTRY_ATTR: "value",
                ENTRY_FUNC: None,
            },
            CALL_TYPE_WRITE_REGISTERS: {
                ENTRY_ATTR: "count",
                ENTRY_FUNC: None,
            },
        }

    def _log_error(self, exception_error: ModbusException, error_state=True):
        log_text = "Pymodbus: " + str(exception_error)
        if self._in_error:
            _LOGGER.debug(log_text)
        else:
            _LOGGER.error(log_text)
            self._in_error = error_state

    async def async_setup(self):
        """Set up pymodbus client."""
        try:
            if self._config_type == "serial":
                self._client = ModbusSerialClient(
                    method=self._config_method,
                    port=self._config_port,
                    baudrate=self._config_baudrate,
                    stopbits=self._config_stopbits,
                    bytesize=self._config_bytesize,
                    parity=self._config_parity,
                    timeout=self._config_timeout,
                    retry_on_empty=True,
                    reset_socket=self._config_reset_socket,
                )
            elif self._config_type == "rtuovertcp":
                self._client = ModbusTcpClient(
                    host=self._config_host,
                    port=self._config_port,
                    framer=ModbusRtuFramer,
                    timeout=self._config_timeout,
                    reset_socket=self._config_reset_socket,
                )
            elif self._config_type == "tcp":
                self._client = ModbusTcpClient(
                    host=self._config_host,
                    port=self._config_port,
                    timeout=self._config_timeout,
                    reset_socket=self._config_reset_socket,
                )
            elif self._config_type == "udp":
                self._client = ModbusUdpClient(
                    host=self._config_host,
                    port=self._config_port,
                    timeout=self._config_timeout,
                    reset_socket=self._config_reset_socket,
                )
        except ModbusException as exception_error:
            self._log_error(exception_error, error_state=False)
            return

        async with self._lock:
            await self.hass.async_add_executor_job(self._pymodbus_connect)

        self._call_type[CALL_TYPE_COIL][ENTRY_FUNC] = self._client.read_coils
        self._call_type[CALL_TYPE_DISCRETE][
            ENTRY_FUNC] = self._client.read_discrete_inputs
        self._call_type[CALL_TYPE_REGISTER_HOLDING][
            ENTRY_FUNC] = self._client.read_holding_registers
        self._call_type[CALL_TYPE_REGISTER_INPUT][
            ENTRY_FUNC] = self._client.read_input_registers
        self._call_type[CALL_TYPE_WRITE_COIL][
            ENTRY_FUNC] = self._client.write_coil
        self._call_type[CALL_TYPE_WRITE_COILS][
            ENTRY_FUNC] = self._client.write_coils
        self._call_type[CALL_TYPE_WRITE_REGISTER][
            ENTRY_FUNC] = self._client.write_register
        self._call_type[CALL_TYPE_WRITE_REGISTERS][
            ENTRY_FUNC] = self._client.write_registers

        # Start counting down to allow modbus requests.
        if self._config_delay:
            self._async_cancel_listener = async_call_later(
                self.hass, self._config_delay, self.async_end_delay)

    @callback
    def async_end_delay(self, args):
        """End startup delay."""
        self._async_cancel_listener = None
        self._config_delay = 0

    def _pymodbus_close(self):
        """Close sync. pymodbus."""
        if self._client:
            try:
                self._client.close()
            except ModbusException as exception_error:
                self._log_error(exception_error)
        self._client = None

    async def async_close(self):
        """Disconnect client."""
        if self._async_cancel_listener:
            self._async_cancel_listener()
            self._async_cancel_listener = None

        async with self._lock:
            return await self.hass.async_add_executor_job(self._pymodbus_close)

    def _pymodbus_connect(self):
        """Connect client."""
        try:
            self._client.connect()
        except ModbusException as exception_error:
            self._log_error(exception_error, error_state=False)

    def _pymodbus_call(self, unit, address, value, use_call):
        """Call sync. pymodbus."""
        kwargs = {"unit": unit} if unit else {}
        try:
            result = self._call_type[use_call][ENTRY_FUNC](address, value,
                                                           **kwargs)
        except ModbusException as exception_error:
            self._log_error(exception_error)
            result = exception_error
        if not hasattr(result, self._call_type[use_call][ENTRY_ATTR]):
            self._log_error(result)
            return None
        self._in_error = False
        return result

    async def async_pymodbus_call(self, unit, address, value, use_call):
        """Convert async to sync pymodbus call."""
        if self._config_delay:
            return None
        async with self._lock:
            return await self.hass.async_add_executor_job(
                self._pymodbus_call, unit, address, value, use_call)
예제 #11
0
            'mean_value': 600,
            'noise_range': 50,
            'always_positive': False
        },
        'temperature': {
            'binary': False,
            'mean_value': 60,
            'noise_range': 5,
            'always_positive': False
        },
        'on_off': {
            'binary': True,
            'mean_value': 0,
            'probability': .1
        }
    }
    fakePLC = FakePLC(8080, example_reading_structure)
    # Modbus UDP client from pymodbus package
    client = ModbusClient('localhost', port=8080)
    plcThread = Thread(target=fakePLC.start_server, daemon=True)
    plcThread.start()
    client.connect()
    while True:
        try:
            sleep(2)
            response = client.read_holding_registers(0, 3)
            print('Response registers:', response.registers)
        except KeyboardInterrupt:
            print('Quiting...')
            exit(0)
예제 #12
0
class ModbusHub:
    """Thread safe wrapper class for pymodbus."""
    def __init__(self, client_config):
        """Initialize the modbus hub."""
        # generic configuration
        self._client = None
        self._kwargs = {'unit': client_config[CONF_MASTER_UNIT_ID]}
        self._lock = threading.Lock()
        self._config_type = client_config[CONF_TYPE]
        self._config_port = client_config[CONF_PORT]
        self._config_timeout = client_config[CONF_TIMEOUT]
        self._config_delay = 0

        if self._config_type == "serial":
            # serial configuration
            self._config_method = client_config[CONF_METHOD]
            self._config_baudrate = client_config[CONF_BAUDRATE]
            self._config_stopbits = client_config[CONF_STOPBITS]
            self._config_bytesize = client_config[CONF_BYTESIZE]
            self._config_parity = client_config[CONF_PARITY]
        else:
            # network configuration
            self._config_host = client_config[CONF_HOST]

    def setup(self):
        """Set up pymodbus client."""
        if self._config_type == "serial":
            from pymodbus.client.sync import ModbusSerialClient
            self._client = ModbusSerialClient(
                method=self._config_method,
                port=self._config_port,
                baudrate=self._config_baudrate,
                stopbits=self._config_stopbits,
                bytesize=self._config_bytesize,
                parity=self._config_parity,
                timeout=self._config_timeout,
                retry_on_empty=True,
            )
        elif self._config_type == "rtuovertcp":
            from pymodbus.client.sync import ModbusTcpClient
            from pymodbus.transaction import ModbusRtuFramer
            self._client = ModbusTcpClient(
                host=self._config_host,
                port=self._config_port,
                framer=ModbusRtuFramer,
                timeout=self._config_timeout,
            )
        elif self._config_type == "tcp":
            from pymodbus.client.sync import ModbusTcpClient
            self._client = ModbusTcpClient(
                host=self._config_host,
                port=self._config_port,
                timeout=self._config_timeout,
            )
        elif self._config_type == "udp":
            from pymodbus.client.sync import ModbusUdpClient
            self._client = ModbusUdpClient(
                host=self._config_host,
                port=self._config_port,
                timeout=self._config_timeout,
            )
        else:
            raise ValueError(("Unsupported config_type, must be serial, " +
                              "tcp, udp, rtuovertcp"))

        # Connect device
        self.connect()

    def close(self):
        """Disconnect client."""
        with self._lock:
            self._client.close()

    def connect(self):
        """Connect client."""
        with self._lock:
            self._client.connect()

    def read_coils(self, address, count=1):
        """Read coils."""
        with self._lock:
            return self._client.read_coils(address, count, **self._kwargs)

    def read_input_registers(self, address, count=1):
        """Read input registers."""
        with self._lock:
            return self._client.read_input_registers(address, count,
                                                     **self._kwargs)

    def read_holding_registers(self, address, count=1):
        """Read holding registers."""
        with self._lock:
            return self._client.read_holding_registers(address, count,
                                                       **self._kwargs)

    def write_coil(self, address, value):
        """Write coil."""
        with self._lock:
            self._client.write_coil(address, value, **self._kwargs)

    def write_register(self, address, value):
        """Write register."""
        with self._lock:
            self._client.write_register(address, value, **self._kwargs)

    def write_registers(self, address, values):
        """Write registers."""
        with self._lock:
            self._client.write_registers(address, values, **self._kwargs)
예제 #13
0
class ModbusHub:
    """Thread safe wrapper class for pymodbus."""

    def __init__(self, client_config):
        """Initialize the Modbus hub."""

        # generic configuration
        self._client = None
        self._in_error = False
        self._lock = threading.Lock()
        self._config_name = client_config[CONF_NAME]
        self._config_type = client_config[CONF_TYPE]
        self._config_port = client_config[CONF_PORT]
        self._config_timeout = client_config[CONF_TIMEOUT]
        self._config_delay = 0

        Defaults.Timeout = 10
        if self._config_type == "serial":
            # serial configuration
            self._config_method = client_config[CONF_METHOD]
            self._config_baudrate = client_config[CONF_BAUDRATE]
            self._config_stopbits = client_config[CONF_STOPBITS]
            self._config_bytesize = client_config[CONF_BYTESIZE]
            self._config_parity = client_config[CONF_PARITY]
        else:
            # network configuration
            self._config_host = client_config[CONF_HOST]
            self._config_delay = client_config[CONF_DELAY]

        if self._config_delay > 0:
            _LOGGER.warning("Parameter delay is accepted but not used in this version")

    @property
    def name(self):
        """Return the name of this hub."""
        return self._config_name

    def _log_error(self, exception_error: ModbusException, error_state=True):
        log_text = "Pymodbus: " + str(exception_error)
        if self._in_error:
            _LOGGER.debug(log_text)
        else:
            _LOGGER.error(log_text)
            self._in_error = error_state

    def setup(self):
        """Set up pymodbus client."""
        try:
            if self._config_type == "serial":
                self._client = ModbusSerialClient(
                    method=self._config_method,
                    port=self._config_port,
                    baudrate=self._config_baudrate,
                    stopbits=self._config_stopbits,
                    bytesize=self._config_bytesize,
                    parity=self._config_parity,
                    timeout=self._config_timeout,
                    retry_on_empty=True,
                )
            elif self._config_type == "rtuovertcp":
                self._client = ModbusTcpClient(
                    host=self._config_host,
                    port=self._config_port,
                    framer=ModbusRtuFramer,
                    timeout=self._config_timeout,
                )
            elif self._config_type == "tcp":
                self._client = ModbusTcpClient(
                    host=self._config_host,
                    port=self._config_port,
                    timeout=self._config_timeout,
                )
            elif self._config_type == "udp":
                self._client = ModbusUdpClient(
                    host=self._config_host,
                    port=self._config_port,
                    timeout=self._config_timeout,
                )
        except ModbusException as exception_error:
            self._log_error(exception_error, error_state=False)
            return

        # Connect device
        self.connect()

    def close(self):
        """Disconnect client."""
        with self._lock:
            try:
                if self._client:
                    self._client.close()
                    self._client = None
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return

    def connect(self):
        """Connect client."""
        with self._lock:
            try:
                self._client.connect()
            except ModbusException as exception_error:
                self._log_error(exception_error, error_state=False)
                return

    def read_coils(self, unit, address, count):
        """Read coils."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.read_coils(address, count, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return None
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return None
            self._in_error = False
            return result

    def read_discrete_inputs(self, unit, address, count):
        """Read discrete inputs."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.read_discrete_inputs(address, count, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return None
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return None
            self._in_error = False
            return result

    def read_input_registers(self, unit, address, count):
        """Read input registers."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.read_input_registers(address, count, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return None
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return None
            self._in_error = False
            return result

    def read_holding_registers(self, unit, address, count):
        """Read holding registers."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.read_holding_registers(address, count, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return None
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return None
            self._in_error = False
            return result

    def write_coil(self, unit, address, value) -> bool:
        """Write coil."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.write_coil(address, value, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return False
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return False
            self._in_error = False
            return True

    def write_coils(self, unit, address, values) -> bool:
        """Write coil."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.write_coils(address, values, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return False
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return False
            self._in_error = False
            return True

    def write_register(self, unit, address, value) -> bool:
        """Write register."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.write_register(address, value, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return False
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return False
            self._in_error = False
            return True

    def write_registers(self, unit, address, values) -> bool:
        """Write registers."""
        with self._lock:
            kwargs = {"unit": unit} if unit else {}
            try:
                result = self._client.write_registers(address, values, **kwargs)
            except ModbusException as exception_error:
                self._log_error(exception_error)
                return False
            if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)):
                self._log_error(result)
                return False
            self._in_error = False
            return True