ucapi 0.3.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {ucapi-0.3.2 → ucapi-0.4.0}/CHANGELOG.md +12 -0
  2. {ucapi-0.3.2/ucapi.egg-info → ucapi-0.4.0}/PKG-INFO +2 -2
  3. {ucapi-0.3.2 → ucapi-0.4.0}/pyproject.toml +1 -1
  4. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/_version.py +3 -3
  5. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/api.py +30 -10
  6. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/api_definitions.py +10 -0
  7. {ucapi-0.3.2 → ucapi-0.4.0/ucapi.egg-info}/PKG-INFO +2 -2
  8. {ucapi-0.3.2 → ucapi-0.4.0}/CONTRIBUTING.md +0 -0
  9. {ucapi-0.3.2 → ucapi-0.4.0}/LICENSE +0 -0
  10. {ucapi-0.3.2 → ucapi-0.4.0}/README.md +0 -0
  11. {ucapi-0.3.2 → ucapi-0.4.0}/docs/code_guidelines.md +0 -0
  12. {ucapi-0.3.2 → ucapi-0.4.0}/docs/setup.md +0 -0
  13. {ucapi-0.3.2 → ucapi-0.4.0}/examples/README.md +0 -0
  14. {ucapi-0.3.2 → ucapi-0.4.0}/examples/hello_integration.json +0 -0
  15. {ucapi-0.3.2 → ucapi-0.4.0}/examples/hello_integration.py +0 -0
  16. {ucapi-0.3.2 → ucapi-0.4.0}/examples/remote.json +0 -0
  17. {ucapi-0.3.2 → ucapi-0.4.0}/examples/remote.py +0 -0
  18. {ucapi-0.3.2 → ucapi-0.4.0}/examples/remote_ui_page.json +0 -0
  19. {ucapi-0.3.2 → ucapi-0.4.0}/examples/setup_flow.json +0 -0
  20. {ucapi-0.3.2 → ucapi-0.4.0}/examples/setup_flow.py +0 -0
  21. {ucapi-0.3.2 → ucapi-0.4.0}/requirements.txt +0 -0
  22. {ucapi-0.3.2 → ucapi-0.4.0}/setup.cfg +0 -0
  23. {ucapi-0.3.2 → ucapi-0.4.0}/test-requirements.txt +0 -0
  24. {ucapi-0.3.2 → ucapi-0.4.0}/tests/test_api.py +0 -0
  25. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/__init__.py +0 -0
  26. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/button.py +0 -0
  27. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/climate.py +0 -0
  28. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/cover.py +0 -0
  29. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/entities.py +0 -0
  30. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/entity.py +0 -0
  31. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/light.py +0 -0
  32. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/media_player.py +0 -0
  33. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/remote.py +0 -0
  34. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/sensor.py +0 -0
  35. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/switch.py +0 -0
  36. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi/ui.py +0 -0
  37. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi.egg-info/SOURCES.txt +0 -0
  38. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi.egg-info/dependency_links.txt +0 -0
  39. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi.egg-info/requires.txt +0 -0
  40. {ucapi-0.3.2 → ucapi-0.4.0}/ucapi.egg-info/top_level.txt +0 -0
@@ -11,6 +11,18 @@ _Changes in the next release_
11
11
 
12
12
  ---
13
13
 
14
+ ## v0.4.0 - 2025-11-24
15
+ ### Breaking Changes
16
+ - A WebSocket disconnection no longer emits the `DISCONNECT` event, but the new `CLIENT_DISCONNECTED` event ([#35](https://github.com/unfoldedcircle/integration-python-library/pull/35)).
17
+
18
+ ### Added
19
+ - New `CLIENT_CONNECTED` event is emitted when a WebSocket client connects ([#35](https://github.com/unfoldedcircle/integration-python-library/pull/35)).
20
+ - WebSocket client identification in disconnect log statements.
21
+
22
+ ### Fixed
23
+ - Null reference exception in log filter ([#33](https://github.com/unfoldedcircle/integration-python-library/pull/33)).
24
+ - Set changed size during iteration for WS broadcast ([#36](https://github.com/unfoldedcircle/integration-python-library/pull/36)).
25
+
14
26
  ## v0.3.2 - 2025-09-17
15
27
  ### Changed
16
28
  - Add support for IR Emitter EntityType ([#31](https://github.com/unfoldedcircle/integration-python-library/pull/31)).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucapi
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Python wrapper for the Unfolded Circle Integration API
5
5
  Author-email: Unfolded Circle ApS <hello@unfoldedcircle.com>
6
6
  License: MPL-2.0
@@ -10,7 +10,7 @@ Project-URL: Bug Reports, https://github.com/unfoldedcircle/integration-python-l
10
10
  Project-URL: Discord, http://unfolded.chat/
11
11
  Project-URL: Forum, https://unfolded.community/
12
12
  Platform: any
13
- Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
16
16
  Classifier: Operating System :: OS Independent
@@ -10,7 +10,7 @@ authors = [
10
10
  license = {text = "MPL-2.0"}
11
11
  description = "Python wrapper for the Unfolded Circle Integration API"
12
12
  classifiers = [
13
- "Development Status :: 3 - Alpha",
13
+ "Development Status :: 4 - Beta",
14
14
  "Intended Audience :: Developers",
15
15
  "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
16
16
  "Operating System :: OS Independent",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.2'
32
- __version_tuple__ = version_tuple = (0, 3, 2)
31
+ __version__ = version = '0.4.0'
32
+ __version_tuple__ = version_tuple = (0, 4, 0)
33
33
 
34
- __commit_id__ = commit_id = 'g098f4fb3e'
34
+ __commit_id__ = commit_id = 'gca3836121'
@@ -162,24 +162,35 @@ class IntegrationAPI:
162
162
  # authenticate on connection
163
163
  await self._authenticate(websocket, True)
164
164
 
165
+ self._events.emit(uc.Events.CLIENT_CONNECTED)
166
+
165
167
  async for message in websocket:
166
168
  # process message
167
169
  await self._process_ws_message(websocket, message)
168
170
 
169
171
  except ConnectionClosedOK:
170
- _LOG.info("WS: Connection closed")
172
+ _LOG.info("[%s] WS: Connection closed", websocket.remote_address)
171
173
 
172
174
  except websockets.exceptions.ConnectionClosedError as e:
173
175
  # 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
+ )
175
182
 
176
183
  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
+ )
178
189
 
179
190
  finally:
180
191
  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)
183
194
 
184
195
  async def _send_ok_result(
185
196
  self, websocket, req_id: int, msg_data: dict[str, Any] | list | None = None
@@ -273,7 +284,7 @@ class IntegrationAPI:
273
284
  data = {"kind": "event", "msg": msg, "msg_data": msg_data, "cat": category}
274
285
  data_dump = json.dumps(data)
275
286
 
276
- for websocket in self._clients:
287
+ for websocket in self._clients.copy():
277
288
  if _LOG.isEnabledFor(logging.DEBUG):
278
289
  _LOG.debug(
279
290
  "[%s] =>: %s", websocket.remote_address, filter_log_msg_data(data)
@@ -763,6 +774,11 @@ class IntegrationAPI:
763
774
  # Properties #
764
775
  ##############
765
776
 
777
+ @property
778
+ def client_count(self) -> int:
779
+ """Return number of WebSocket clients."""
780
+ return len(self._clients)
781
+
766
782
  @property
767
783
  def device_state(self) -> uc.DeviceStates:
768
784
  """
@@ -887,9 +903,12 @@ def filter_log_msg_data(data: dict[str, Any]) -> dict[str, Any]:
887
903
  if (
888
904
  "attributes" in log_upd["msg_data"]
889
905
  and MediaAttr.MEDIA_IMAGE_URL in log_upd["msg_data"]["attributes"]
890
- and log_upd["msg_data"]["attributes"][MediaAttr.MEDIA_IMAGE_URL].startswith(
891
- "data:"
906
+ and (
907
+ media_image_url := log_upd["msg_data"]["attributes"][
908
+ MediaAttr.MEDIA_IMAGE_URL
909
+ ]
892
910
  )
911
+ and media_image_url.startswith("data:")
893
912
  ):
894
913
  log_upd["msg_data"]["attributes"][MediaAttr.MEDIA_IMAGE_URL] = "data:***"
895
914
  elif isinstance(log_upd["msg_data"], list):
@@ -897,9 +916,10 @@ def filter_log_msg_data(data: dict[str, Any]) -> dict[str, Any]:
897
916
  if (
898
917
  "attributes" in item
899
918
  and MediaAttr.MEDIA_IMAGE_URL in item["attributes"]
900
- and item["attributes"][MediaAttr.MEDIA_IMAGE_URL].startswith(
901
- "data:"
919
+ and (
920
+ media_image_url := item["attributes"][MediaAttr.MEDIA_IMAGE_URL]
902
921
  )
922
+ and media_image_url.startswith("data:")
903
923
  ):
904
924
  item["attributes"][MediaAttr.MEDIA_IMAGE_URL] = "data:***"
905
925
 
@@ -82,13 +82,23 @@ class WsMsgEvents(str, Enum):
82
82
  class Events(str, Enum):
83
83
  """Internal library events."""
84
84
 
85
+ CLIENT_CONNECTED = "client_connected"
86
+ """WebSocket client connected."""
87
+ CLIENT_DISCONNECTED = "client_disconnected"
88
+ """WebSocket client disconnected."""
85
89
  ENTITY_ATTRIBUTES_UPDATED = "entity_attributes_updated"
86
90
  SUBSCRIBE_ENTITIES = "subscribe_entities"
91
+ """Integration API `subscribe_events` message."""
87
92
  UNSUBSCRIBE_ENTITIES = "unsubscribe_entities"
93
+ """Integration API `unsubscribe_events` message."""
88
94
  CONNECT = "connect"
95
+ """Integration-API `connect` event message."""
89
96
  DISCONNECT = "disconnect"
97
+ """Integration-API `disconnect` event message."""
90
98
  ENTER_STANDBY = "enter_standby"
99
+ """Integration-API `enter_standby` event message."""
91
100
  EXIT_STANDBY = "exit_standby"
101
+ """Integration-API `exit_standby` event message."""
92
102
 
93
103
 
94
104
  # Does EventCategory need to be public?
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ucapi
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Python wrapper for the Unfolded Circle Integration API
5
5
  Author-email: Unfolded Circle ApS <hello@unfoldedcircle.com>
6
6
  License: MPL-2.0
@@ -10,7 +10,7 @@ Project-URL: Bug Reports, https://github.com/unfoldedcircle/integration-python-l
10
10
  Project-URL: Discord, http://unfolded.chat/
11
11
  Project-URL: Forum, https://unfolded.community/
12
12
  Platform: any
13
- Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
16
16
  Classifier: Operating System :: OS Independent
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes