ucapi 0.3.1__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.
- {ucapi-0.3.1 → ucapi-0.4.0}/CHANGELOG.md +19 -2
- {ucapi-0.3.1/ucapi.egg-info → ucapi-0.4.0}/PKG-INFO +2 -2
- {ucapi-0.3.1 → ucapi-0.4.0}/pyproject.toml +1 -1
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/_version.py +16 -3
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/api.py +30 -10
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/api_definitions.py +10 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/entity.py +1 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/ui.py +3 -0
- {ucapi-0.3.1 → ucapi-0.4.0/ucapi.egg-info}/PKG-INFO +2 -2
- {ucapi-0.3.1 → ucapi-0.4.0}/CONTRIBUTING.md +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/LICENSE +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/README.md +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/docs/code_guidelines.md +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/docs/setup.md +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/README.md +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/hello_integration.json +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/hello_integration.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/remote.json +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/remote.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/remote_ui_page.json +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/setup_flow.json +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/examples/setup_flow.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/requirements.txt +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/setup.cfg +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/test-requirements.txt +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/tests/test_api.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/__init__.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/button.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/climate.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/cover.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/entities.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/light.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/media_player.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/remote.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/sensor.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi/switch.py +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi.egg-info/SOURCES.txt +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi.egg-info/dependency_links.txt +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi.egg-info/requires.txt +0 -0
- {ucapi-0.3.1 → ucapi-0.4.0}/ucapi.egg-info/top_level.txt +0 -0
|
@@ -11,11 +11,28 @@ _Changes in the next release_
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## v0.
|
|
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
|
+
|
|
26
|
+
## v0.3.2 - 2025-09-17
|
|
27
|
+
### Changed
|
|
28
|
+
- Add support for IR Emitter EntityType ([#31](https://github.com/unfoldedcircle/integration-python-library/pull/31)).
|
|
29
|
+
- Add stop, record and menu for remote entity buttons ([#32](https://github.com/unfoldedcircle/integration-python-library/pull/32)).
|
|
30
|
+
|
|
31
|
+
## v0.3.1 - 2025-05-14
|
|
15
32
|
### Fixed
|
|
16
33
|
- Filtered log messages may not modify original data. This sporadically removed media artwork URLs ([#27](https://github.com/unfoldedcircle/integration-python-library/pull/27)).
|
|
17
34
|
|
|
18
|
-
## v0.3.0 -
|
|
35
|
+
## v0.3.0 - 2025-04-25
|
|
19
36
|
### Added
|
|
20
37
|
- New media-player attribute MEDIA_POSITION_UPDATED_AT ([feature-and-bug-tracker#443](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/443)).
|
|
21
38
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ucapi
|
|
3
|
-
Version: 0.
|
|
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 ::
|
|
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 ::
|
|
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",
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '0.
|
|
21
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.4.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 4, 0)
|
|
33
|
+
|
|
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(
|
|
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(
|
|
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.
|
|
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
|
|
891
|
-
"
|
|
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
|
|
901
|
-
"
|
|
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
|
+
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 ::
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|