def testUdpClientIsSocketOpen(self): ''' Test the udp client is_socket_open method''' client = ModbusUdpClient() self.assertTrue(client.is_socket_open())
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] self._config_retries = client_config[CONF_RETRIES] self._config_retry_on_empty = client_config[CONF_RETRY_ON_EMPTY] 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, text: str, error_state=True): log_text = f"Pymodbus: {text}" 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, reset_socket=self._config_reset_socket, ) except ModbusException as exception_error: self._log_error(str(exception_error), error_state=False) return False async with self._lock: if not await self.hass.async_add_executor_job(self._pymodbus_connect): self._log_error("initial connect failed, no retry", error_state=False) return False 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 ) return True @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(str(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: return self._client.connect() except ModbusException as exception_error: self._log_error(str(exception_error), error_state=False) return 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(str(exception_error)) return None if not hasattr(result, self._call_type[use_call][ENTRY_ATTR]): self._log_error(str(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 if not self._client.is_socket_open(): return None async with self._lock: result = await self.hass.async_add_executor_job( self._pymodbus_call, unit, address, value, use_call ) if self._config_type == "serial": # small delay until next request/response await asyncio.sleep(30 / 1000) return result