pyisyox.runtime.ws module¶
WebSocket reader loop for the eisy event stream.
Opens wss://{host}/rest/subscribe (or another configured path) using
the same pyisyox.auth.Auth strategy the HTTP client uses, then
runs a read loop that feeds every frame to an
EventDispatcher. Reconnects with
exponential backoff on transport errors; refreshes auth tokens on a
401-class WebSocket handshake failure.
Auth integration:
LocalAuthreturns{"auth": aiohttp.BasicAuth(...)}fromrequest_kwargs— aiohttp’sws_connectacceptsauthdirectly, so the upgrade carries anAuthorization: Basicheader.PortalAuthreturns{"headers": {"Authorization": "Bearer ..."}}.ws_connectpassesheadersthrough verbatim, so the bearer rides on the upgrade.
The loop is intentionally split from the parsing/dispatch logic in
pyisyox.runtime.events so the dispatcher can be unit-tested
without WebSocket plumbing and the reader can be unit-tested without a
real WS server.
- class WebSocketEventStream(client, dispatcher, path='/rest/subscribe')[source]¶
Bases:
objectBackground reader that feeds frames into an
EventDispatcher.Lifecycle:
start()schedules the read task and returns immediately.The task connects, dispatches frames, reconnects on transport errors, and pumps
EventStreamStatusnotifications to any registered status listener. On each connect it holdsSYNCING(notCONNECTED) until the controller’s initial status replay drains, so consumers don’t treat the replay as live events.stop()cancels the task and closes any active WS.
The class deliberately keeps its surface narrow — the consumer is expected to be the top-level
ISYglue object that owns both theIoXClientand the dispatcher.- Parameters:
client (IoXClient)
dispatcher (EventDispatcher)
path (str)
- property status: EventStreamStatus¶
Most-recent stream status.
Updated on every transition (initialise / connect / reconnect / disconnect / lost). Defaults to
EventStreamStatus.NOT_STARTEDbeforestart(). Useful for system-health pages that want a single readable status string without subscribing to every notification.
- property connected: bool¶
Truewhile the stream is in theCONNECTEDstate.Convenience over comparing
statusdirectly. Note thatconnectedflippingFalsedoesn’t mean the reader has given up — it may be reconnecting, or inEventStreamStatus.SYNCING(socket open but the controller’s initial status replay hasn’t drained yet — intentionally not “connected” so event consumers don’t treat the replay as live changes).
- property last_event_at: datetime | None¶
UTC timestamp of the most recent text frame, or
Noneif no frame has been received this lifetime.The eisy emits a heartbeat
<control>_0</control>frame every 30 seconds even when nothing else changes, so a stalelast_event_at(more than ~60 s ago) is a reasonable signal that the connection is broken even when the WS handshake hasn’t returned an error yet.
- add_status_listener(callback)[source]¶
Register a callback for stream-status changes.
- Returns:
An unsubscribe function.
- Parameters:
callback (Callable[[EventStreamStatus], None])
- Return type:
Callable[[], None]