herosdevices.hardware.menlo.qwebchannel
=======================================

.. py:module:: herosdevices.hardware.menlo.qwebchannel

.. autoapi-nested-parse::

   Client for Qt's WebChannel protocol over a plain websocket, without any Qt dependency.

   Some instruments (for example Menlo Systems frequency combs) expose their control/status tree via Qt's
   `QWebChannel <https://doc.qt.io/qt-5/qtwebchannel-index.html>`_ protocol. The protocol implementation itself
   is provided by the :mod:`pywebchannel` package, which has no Qt dependency. This module only supplies the
   websocket transport and the endpoint's authentication handshake on top of it.







Module Contents
---------------

.. py:data:: QWebChannel
   :value: None


.. py:data:: QWEBCHANNEL_DEFAULT_PORT
   :value: 8002


.. py:class:: QWebChannelConnection(host: str, port: int = QWEBCHANNEL_DEFAULT_PORT, path: str = '/core/', user: str = 'guest', password: str = '', timeout: float = 5.0, reconnect_cooldown: float = 30.0)

   Manage a QWebChannel connection to a device over a plain websocket.

   :param host: Hostname or IP address of the websocket endpoint.
   :param port: Port the websocket endpoint listens on.
   :param path: Path component of the websocket URL.
   :param user: Username used for authentication.
   :param password: Password used for authentication.
   :param timeout: Seconds to wait for the connection to open and the channel to initialize. Also bounds how
                   long the underlying socket connect may block, so an unresponsive (as opposed to actively
                   refusing) endpoint fails within `timeout` too, instead of hanging on the OS's own connect timeout.
   :param reconnect_cooldown: Minimum seconds between two connection attempts. A :py:meth:`connect` call made
                              before this has elapsed since the last attempt fails immediately without touching the network,
                              so a caller that retries on every use (like :class:`OFC`) does not hammer a device that
                              stays unreachable for an extended period.

   .. note::

      There is no active reconnect-on-disconnect: a lost connection is only noticed and re-established the
      next time :py:meth:`connect` is called (which every read/write on :class:`OFC` does implicitly
      via its `_ensure_connected` helper). This keeps reconnection logic in one place, serialized by a lock,
      instead of racing an active retry from the websocket's own background thread against callers.


   .. py:attribute:: host


   .. py:attribute:: port
      :value: 8002



   .. py:attribute:: path
      :value: '/core/'



   .. py:attribute:: user
      :value: 'guest'



   .. py:attribute:: password
      :value: ''



   .. py:attribute:: timeout
      :value: 5.0



   .. py:attribute:: reconnect_cooldown
      :value: 30.0



   .. py:attribute:: channel


   .. py:property:: root

      The root object exposed by the QWebChannel endpoint, or None if not connected yet.


   .. py:method:: is_ready() -> bool

      Whether the channel is initialized and :py:attr:`root` is available.



   .. py:method:: send(data: str) -> None

      Send raw data over the websocket.

      Called by :py:class:`pywebchannel.qwebchannel.QWebChannel`, not meant to be called directly.



   .. py:method:: connect() -> None

      Open the websocket connection and wait until the channel is initialized.

      Does nothing if a connection is already open and ready. Safe to call concurrently from multiple
      threads: only the first caller actually (re)connects, the others wait for it and then observe the
      result.

      :raises ConnectionError: The last attempt was less than `reconnect_cooldown` seconds ago; no new attempt
          was made.
      :raises TimeoutError: The connection could not be established within `timeout` seconds.



   .. py:method:: close() -> None

      Close the websocket connection.



