Skip to content

Commit 41b958d

Browse files
authored
refactor!: WS client disconnection may not emit a disconnect event (#35)
The `disconnect` event is defined in the Integration-API and may not be used to signal a WebSocket disconnection. New events have been introduced to listen to WS connections and disconnections and a new `client_count` property to get the number of clients.
1 parent 35840da commit 41b958d

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
_Changes in the next release_
1111

12+
### Breaking Changes
13+
- A WebSocket disconnection no longer emits the `DISCONNECT` event, but the new `CLIENT_DISCONNECTED` event.
14+
15+
### Added
16+
- New `CLIENT_CONNECTED` event is emitted when a WebSocket client connects.
17+
- WebSocket client identification in disconnect log statements.
18+
1219
---
1320

1421
## v0.3.2 - 2025-09-17

ucapi/api.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,24 +162,35 @@ async def _handle_ws(self, websocket) -> None:
162162
# authenticate on connection
163163
await self._authenticate(websocket, True)
164164

165+
self._events.emit(uc.Events.CLIENT_CONNECTED)
166+
165167
async for message in websocket:
166168
# process message
167169
await self._process_ws_message(websocket, message)
168170

169171
except ConnectionClosedOK:
170-
_LOG.info("WS: Connection closed")
172+
_LOG.info("[%s] WS: Connection closed", websocket.remote_address)
171173

172174
except websockets.exceptions.ConnectionClosedError as e:
173175
# no idea why they made code & reason deprecated...
174-
_LOG.info("WS: Connection closed with error %d: %s", e.code, e.reason)
176+
_LOG.info(
177+
"[%s] WS: Connection closed with error %d: %s",
178+
websocket.remote_address,
179+
e.code,
180+
e.reason,
181+
)
175182

176183
except websockets.exceptions.WebSocketException as e:
177-
_LOG.error("WS: Connection closed due to processing error: %s", e)
184+
_LOG.error(
185+
"[%s] WS: Connection closed due to processing error: %s",
186+
websocket.remote_address,
187+
e,
188+
)
178189

179190
finally:
180191
self._clients.remove(websocket)
181-
_LOG.info("WS: Client removed")
182-
self._events.emit(uc.Events.DISCONNECT)
192+
_LOG.info("[%s] WS: Client removed", websocket.remote_address)
193+
self._events.emit(uc.Events.CLIENT_DISCONNECTED)
183194

184195
async def _send_ok_result(
185196
self, websocket, req_id: int, msg_data: dict[str, Any] | list | None = None
@@ -763,6 +774,11 @@ def remove_all_listeners(self, event: uc.Events | None) -> None:
763774
# Properties #
764775
##############
765776

777+
@property
778+
def client_count(self) -> int:
779+
"""Return number of WebSocket clients."""
780+
return len(self._clients)
781+
766782
@property
767783
def device_state(self) -> uc.DeviceStates:
768784
"""

ucapi/api_definitions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,23 @@ class WsMsgEvents(str, Enum):
8282
class Events(str, Enum):
8383
"""Internal library events."""
8484

85+
CLIENT_CONNECTED = "client_connected"
86+
"""WebSocket client connected."""
87+
CLIENT_DISCONNECTED = "client_disconnected"
88+
"""WebSocket client disconnected."""
8589
ENTITY_ATTRIBUTES_UPDATED = "entity_attributes_updated"
8690
SUBSCRIBE_ENTITIES = "subscribe_entities"
91+
"""Integration API `subscribe_events` message."""
8792
UNSUBSCRIBE_ENTITIES = "unsubscribe_entities"
93+
"""Integration API `unsubscribe_events` message."""
8894
CONNECT = "connect"
95+
"""Integration-API `connect` event message."""
8996
DISCONNECT = "disconnect"
97+
"""Integration-API `disconnect` event message."""
9098
ENTER_STANDBY = "enter_standby"
99+
"""Integration-API `enter_standby` event message."""
91100
EXIT_STANDBY = "exit_standby"
101+
"""Integration-API `exit_standby` event message."""
92102

93103

94104
# Does EventCategory need to be public?

0 commit comments

Comments
 (0)