code: def _open_devices(self):
      if self.port is not None:
          self._clisrv = ZmqWrapper.ClientServer(
              port=DefaultPorts.CliSrv + self.port,
              is_server=True,
              callback=self._clisrv_callback,
          )
  
          # notify existing listeners of our ID
          self._zmq_stream_pub = self._stream_factory.get_streams(
              stream_types=["tcp:" + str(self.port)], for_write=True
          )[0]
  
          # ZMQ quirk: we must wait a bit after opening port and before sending message
          # TODO: can we do better?
          self._th = threading.Thread(target=self._send_server_start)
          self._th.start()
  
file_path: tensorwatch/tensorwatch/watcher.py
imports: import uuid
  from typing import Sequence
  from .zmq_wrapper import ZmqWrapper
  from .watcher_base import WatcherBase
  from .lv_types import CliSrvReqTypes
  from .lv_types import DefaultPorts, PublisherTopics, ServerMgmtMsg
  from .None import utils
  import threading, time
label_desc: Use image node_id 2 for calling the Watcher obj's underlying member method code with example usage: obj._open_devices() without return types