ucapi 0.2.0__tar.gz → 0.3.1__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.2.0 → ucapi-0.3.1}/CHANGELOG.md +11 -0
  2. {ucapi-0.2.0 → ucapi-0.3.1}/CONTRIBUTING.md +8 -4
  3. {ucapi-0.2.0/ucapi.egg-info → ucapi-0.3.1}/PKG-INFO +6 -5
  4. {ucapi-0.2.0 → ucapi-0.3.1}/README.md +1 -1
  5. {ucapi-0.2.0 → ucapi-0.3.1}/pyproject.toml +2 -2
  6. {ucapi-0.2.0 → ucapi-0.3.1}/requirements.txt +1 -1
  7. ucapi-0.3.1/tests/test_api.py +95 -0
  8. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/_version.py +9 -4
  9. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/api.py +57 -29
  10. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/climate.py +2 -1
  11. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/cover.py +2 -1
  12. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/entity.py +1 -0
  13. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/light.py +2 -1
  14. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/media_player.py +3 -1
  15. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/remote.py +2 -1
  16. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/sensor.py +2 -1
  17. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/switch.py +2 -1
  18. {ucapi-0.2.0 → ucapi-0.3.1/ucapi.egg-info}/PKG-INFO +6 -5
  19. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi.egg-info/SOURCES.txt +1 -0
  20. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi.egg-info/requires.txt +1 -1
  21. {ucapi-0.2.0 → ucapi-0.3.1}/LICENSE +0 -0
  22. {ucapi-0.2.0 → ucapi-0.3.1}/docs/code_guidelines.md +0 -0
  23. {ucapi-0.2.0 → ucapi-0.3.1}/docs/setup.md +0 -0
  24. {ucapi-0.2.0 → ucapi-0.3.1}/examples/README.md +0 -0
  25. {ucapi-0.2.0 → ucapi-0.3.1}/examples/hello_integration.json +0 -0
  26. {ucapi-0.2.0 → ucapi-0.3.1}/examples/hello_integration.py +0 -0
  27. {ucapi-0.2.0 → ucapi-0.3.1}/examples/remote.json +0 -0
  28. {ucapi-0.2.0 → ucapi-0.3.1}/examples/remote.py +0 -0
  29. {ucapi-0.2.0 → ucapi-0.3.1}/examples/remote_ui_page.json +0 -0
  30. {ucapi-0.2.0 → ucapi-0.3.1}/examples/setup_flow.json +0 -0
  31. {ucapi-0.2.0 → ucapi-0.3.1}/examples/setup_flow.py +0 -0
  32. {ucapi-0.2.0 → ucapi-0.3.1}/setup.cfg +0 -0
  33. {ucapi-0.2.0 → ucapi-0.3.1}/test-requirements.txt +0 -0
  34. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/__init__.py +0 -0
  35. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/api_definitions.py +0 -0
  36. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/button.py +0 -0
  37. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/entities.py +0 -0
  38. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi/ui.py +0 -0
  39. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi.egg-info/dependency_links.txt +0 -0
  40. {ucapi-0.2.0 → ucapi-0.3.1}/ucapi.egg-info/top_level.txt +0 -0
@@ -11,6 +11,17 @@ _Changes in the next release_
11
11
 
12
12
  ---
13
13
 
14
+ ## v0.3.1 - 2024-05-14
15
+ ### Fixed
16
+ - 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
+
18
+ ## v0.3.0 - 2024-04-25
19
+ ### Added
20
+ - New media-player attribute MEDIA_POSITION_UPDATED_AT ([feature-and-bug-tracker#443](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/443)).
21
+ ### Changed
22
+ - Filter out base64 encoded media-player image fields in entity_states response log messages ([#22](https://github.com/unfoldedcircle/integration-python-library/issues/22)).
23
+ - Require websockets version v14 or newer.
24
+
14
25
  ## v0.2.0 - 2024-04-28
15
26
  ### Added
16
27
  - New remote-entity type. Requires remote-core / Core Simulator version 0.43.0 or newer.
@@ -25,7 +25,7 @@ We love contributions from everyone.
25
25
  Either by opening a feature request describing your proposed changes before submitting code, or by contacting us on
26
26
  one of the other [feedback channels](#feedback-speech_balloon).
27
27
 
28
- Since this library is being used in integration drivers running on the embedded Remote Two device,
28
+ Since this library is being used in integration drivers running on the embedded UC Remote devices,
29
29
  we have to make sure it remains compatible with the embedded runtime environment and runs smoothly.
30
30
 
31
31
  Submitting pull requests for typos, formatting issues etc. are happily accepted and usually approved relatively quick.
@@ -36,7 +36,8 @@ With that out of the way, here's the process of creating a pull request and maki
36
36
 
37
37
  1. Fork the repo.
38
38
 
39
- 2. Make your changes or enhancements (preferably on a feature-branch).
39
+ 2. Make your changes or enhancements.
40
+ This should be done in a dedicated branch and not on the main branch to easily submit individual pull requests.
40
41
 
41
42
  Contributed code must be licensed under the [Mozilla Public License 2.0](https://choosealicense.com/licenses/mpl-2.0/),
42
43
  or a compatible license, if existing parts of other projects are reused (e.g. MIT licensed code).
@@ -53,13 +54,16 @@ With that out of the way, here's the process of creating a pull request and maki
53
54
 
54
55
  3. Make sure your changes follow the project's code style and the lints pass described in [Code Style](docs/code_guidelines.md).
55
56
 
56
- 4. Push to your fork.
57
+ 4. Push to your fork.
58
+ Do not include any project configuration changes in the `.idea` folder! If you are also using an IntelliJ product,
59
+ chances are that you're using a different IDE, version or other settings, which can cause issues.
60
+ For example, between IntelliJ Ultimate and PyCharm.
57
61
 
58
62
  5. Submit a pull request.
59
63
 
60
64
  At this point we will review the PR and give constructive feedback.
61
65
  This is a time for discussion and improvements, and making the necessary changes will be required before we can
62
- merge the contribution.
66
+ merge the contribution. Furthermore, all the automated checks must pass, otherwise the pull request will not be merged.
63
67
 
64
68
  ### Feedback :speech_balloon:
65
69
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ucapi
3
- Version: 0.2.0
3
+ Version: 0.3.1
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
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://www.unfoldedcircle.com/
8
8
  Project-URL: Source Code, https://github.com/unfoldedcircle/integration-python-library
9
9
  Project-URL: Bug Reports, https://github.com/unfoldedcircle/integration-python-library/issues
10
10
  Project-URL: Discord, http://unfolded.chat/
11
- Project-URL: Forum, http://unfolded.community/
11
+ Project-URL: Forum, https://unfolded.community/
12
12
  Platform: any
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Intended Audience :: Developers
@@ -22,7 +22,7 @@ Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown; charset=UTF-8
23
23
  License-File: LICENSE
24
24
  Requires-Dist: pyee>=9.0
25
- Requires-Dist: websockets>=11.0
25
+ Requires-Dist: websockets>=14.0
26
26
  Requires-Dist: zeroconf>=0.120.0
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pylint; extra == "testing"
@@ -30,13 +30,14 @@ Requires-Dist: flake8-docstrings; extra == "testing"
30
30
  Requires-Dist: flake8; extra == "testing"
31
31
  Requires-Dist: black; extra == "testing"
32
32
  Requires-Dist: isort; extra == "testing"
33
+ Dynamic: license-file
33
34
 
34
35
  # Python API wrapper for the UC Integration API
35
36
  [![PyPi](https://img.shields.io/pypi/v/ucapi.svg)](https://pypi.org/project/ucapi)
36
37
  [![License](https://img.shields.io/github/license/unfoldedcircle/integration-python-library.svg)](LICENSE)
37
38
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
38
39
 
39
- This library simplifies writing Python based integrations for the [Unfolded Circle Remote Two](https://www.unfoldedcircle.com/)
40
+ This library simplifies writing Python-based integrations for the [Unfolded Circle Remote devices](https://www.unfoldedcircle.com/)
40
41
  by wrapping the [WebSocket Integration API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api).
41
42
 
42
43
  It's an alpha release (in our eyes). Breaking changes are to be expected and missing features will be continuously added.
@@ -3,7 +3,7 @@
3
3
  [![License](https://img.shields.io/github/license/unfoldedcircle/integration-python-library.svg)](LICENSE)
4
4
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
5
5
 
6
- This library simplifies writing Python based integrations for the [Unfolded Circle Remote Two](https://www.unfoldedcircle.com/)
6
+ This library simplifies writing Python-based integrations for the [Unfolded Circle Remote devices](https://www.unfoldedcircle.com/)
7
7
  by wrapping the [WebSocket Integration API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api).
8
8
 
9
9
  It's an alpha release (in our eyes). Breaking changes are to be expected and missing features will be continuously added.
@@ -22,7 +22,7 @@ classifiers = [
22
22
  requires-python = ">=3.10"
23
23
  dependencies = [
24
24
  "pyee>=9.0",
25
- "websockets>=11.0",
25
+ "websockets>=14.0",
26
26
  "zeroconf>=0.120.0",
27
27
  ]
28
28
  dynamic = ["version"]
@@ -36,7 +36,7 @@ content-type = "text/markdown; charset=UTF-8"
36
36
  "Source Code" = "https://github.com/unfoldedcircle/integration-python-library"
37
37
  "Bug Reports" = "https://github.com/unfoldedcircle/integration-python-library/issues"
38
38
  "Discord" = "http://unfolded.chat/"
39
- "Forum" = "http://unfolded.community/"
39
+ "Forum" = "https://unfolded.community/"
40
40
 
41
41
  [project.optional-dependencies]
42
42
  testing = [
@@ -3,5 +3,5 @@
3
3
  # Workaround: use a pre-commit hook with https://github.com/scikit-image/scikit-image/blob/main/tools/generate_requirements.py
4
4
 
5
5
  pyee>=9.0
6
- websockets>=11.0
6
+ websockets>=14.0
7
7
  zeroconf>=0.120.0
@@ -0,0 +1,95 @@
1
+ import unittest
2
+ from copy import deepcopy
3
+
4
+ from ucapi.api import filter_log_msg_data
5
+ from ucapi.media_player import Attributes
6
+
7
+
8
+ class TestFilterLogMsgData(unittest.TestCase):
9
+
10
+ def test_no_modification_when_no_msg_data(self):
11
+ data = {}
12
+ result = filter_log_msg_data(data)
13
+ self.assertEqual(result, {}, "The result should be an empty dictionary")
14
+
15
+ def test_no_changes_when_media_image_url_not_present(self):
16
+ data = {"msg_data": {"attributes": {"state": "playing", "volume": 50}}}
17
+ original = deepcopy(data)
18
+
19
+ result = filter_log_msg_data(data)
20
+
21
+ self.assertEqual(
22
+ result,
23
+ original,
24
+ "The result should be the same as the input when no filtered attributes are present",
25
+ )
26
+
27
+ def test_filtering_media_image_url_in_dict(self):
28
+ data = {
29
+ "msg_data": {
30
+ "attributes": {
31
+ "state": "playing",
32
+ Attributes.MEDIA_IMAGE_URL: "data:image/png;base64,encodeddata",
33
+ }
34
+ }
35
+ }
36
+ expected_result = deepcopy(data)
37
+ expected_result["msg_data"]["attributes"][
38
+ Attributes.MEDIA_IMAGE_URL
39
+ ] = "data:***"
40
+
41
+ result = filter_log_msg_data(data)
42
+
43
+ self.assertEqual(
44
+ result, expected_result, "The MEDIA_IMAGE_URL attribute should be filtered"
45
+ )
46
+
47
+ def test_filtering_media_image_url_in_list(self):
48
+ data = {
49
+ "msg_data": [
50
+ {
51
+ "attributes": {
52
+ "state": "paused",
53
+ Attributes.MEDIA_IMAGE_URL: "data:image/png;base64,exampledata",
54
+ }
55
+ },
56
+ {
57
+ "attributes": {
58
+ "state": "playing",
59
+ Attributes.MEDIA_IMAGE_URL: "data:image/jpg;base64,exampledata",
60
+ }
61
+ },
62
+ {"attributes": {"state": "stopped"}},
63
+ ]
64
+ }
65
+ expected_result = deepcopy(data)
66
+ expected_result["msg_data"][0]["attributes"][
67
+ Attributes.MEDIA_IMAGE_URL
68
+ ] = "data:***"
69
+ expected_result["msg_data"][1]["attributes"][
70
+ Attributes.MEDIA_IMAGE_URL
71
+ ] = "data:***"
72
+
73
+ result = filter_log_msg_data(data)
74
+
75
+ self.assertEqual(
76
+ result,
77
+ expected_result,
78
+ "All MEDIA_IMAGE_URL attributes in the list should be filtered",
79
+ )
80
+
81
+ def test_input_is_not_modified(self):
82
+ data = {
83
+ "msg_data": {
84
+ "attributes": {
85
+ Attributes.MEDIA_IMAGE_URL: "data:image/png;base64,encodeddata"
86
+ }
87
+ }
88
+ }
89
+ original_data = deepcopy(data)
90
+
91
+ filter_log_msg_data(data)
92
+
93
+ self.assertEqual(
94
+ data, original_data, "The input data should not be modified by the function"
95
+ )
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.2.0'
16
- __version_tuple__ = version_tuple = (0, 2, 0)
20
+ __version__ = version = '0.3.1'
21
+ __version_tuple__ = version_tuple = (0, 3, 1)
@@ -11,22 +11,28 @@ import logging
11
11
  import os
12
12
  import socket
13
13
  from asyncio import AbstractEventLoop
14
+ from copy import deepcopy
14
15
  from typing import Any, Callable
15
16
 
16
17
  import websockets
17
18
  from pyee.asyncio import AsyncIOEventEmitter
18
19
 
20
+ # Note: websockets v14 doesn't have websockets.server anymore
21
+ from websockets import serve
22
+
19
23
  # workaround for pylint error: E0611: No name 'ConnectionClosedOK' in module 'websockets' (no-name-in-module) # noqa
20
24
  from websockets.exceptions import ConnectionClosedOK
21
-
22
- # workaround for pylint error: E1101: Module 'websockets' has no 'serve' member (no-member) # noqa
23
- from websockets.server import serve
24
25
  from zeroconf import IPVersion
25
26
  from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf
26
27
 
27
28
  import ucapi.api_definitions as uc
28
- from ucapi import media_player
29
29
  from ucapi.entities import Entities
30
+ from ucapi.media_player import Attributes as MediaAttr
31
+
32
+ try:
33
+ from ._version import version as __version__
34
+ except ImportError:
35
+ __version__ = "unknown"
30
36
 
31
37
  _LOG = logging.getLogger(__name__)
32
38
  _LOG.setLevel(logging.DEBUG)
@@ -125,9 +131,10 @@ class IntegrationAPI:
125
131
  )
126
132
 
127
133
  _LOG.info(
128
- "Driver is up: %s, version: %s, listening on: %s:%d",
134
+ "Driver is up: %s, version: %s, api: %s, listening on: %s:%d",
129
135
  self._driver_info["driver_id"],
130
136
  self._driver_info["version"],
137
+ __version__,
131
138
  host,
132
139
  port,
133
140
  )
@@ -163,6 +170,7 @@ class IntegrationAPI:
163
170
  _LOG.info("WS: Connection closed")
164
171
 
165
172
  except websockets.exceptions.ConnectionClosedError as e:
173
+ # no idea why they made code & reason deprecated...
166
174
  _LOG.info("WS: Connection closed with error %d: %s", e.code, e.reason)
167
175
 
168
176
  except websockets.exceptions.WebSocketException as e:
@@ -210,6 +218,7 @@ class IntegrationAPI:
210
218
  """
211
219
  await self._send_ws_response(websocket, req_id, "result", msg_data, status_code)
212
220
 
221
+ # pylint: disable=R0917
213
222
  async def _send_ws_response(
214
223
  self,
215
224
  websocket,
@@ -240,7 +249,10 @@ class IntegrationAPI:
240
249
 
241
250
  if websocket in self._clients:
242
251
  data_dump = json.dumps(data)
243
- _LOG.debug("[%s] ->: %s", websocket.remote_address, data_dump)
252
+ if _LOG.isEnabledFor(logging.DEBUG):
253
+ _LOG.debug(
254
+ "[%s] ->: %s", websocket.remote_address, filter_log_msg_data(data)
255
+ )
244
256
  await websocket.send(data_dump)
245
257
  else:
246
258
  _LOG.error("Error sending response: connection no longer established")
@@ -260,13 +272,12 @@ class IntegrationAPI:
260
272
  """
261
273
  data = {"kind": "event", "msg": msg, "msg_data": msg_data, "cat": category}
262
274
  data_dump = json.dumps(data)
263
- # filter fields
264
- if _LOG.isEnabledFor(logging.DEBUG):
265
- data_log = json.dumps(data) if filter_log_msg_data(data) else data_dump
266
275
 
267
276
  for websocket in self._clients:
268
277
  if _LOG.isEnabledFor(logging.DEBUG):
269
- _LOG.debug("[%s] ->: %s", websocket.remote_address, data_log)
278
+ _LOG.debug(
279
+ "[%s] =>: %s", websocket.remote_address, filter_log_msg_data(data)
280
+ )
270
281
  try:
271
282
  await websocket.send(data_dump)
272
283
  except websockets.exceptions.WebSocketException:
@@ -291,8 +302,9 @@ class IntegrationAPI:
291
302
 
292
303
  if websocket in self._clients:
293
304
  if _LOG.isEnabledFor(logging.DEBUG):
294
- data_log = json.dumps(data) if filter_log_msg_data(data) else data_dump
295
- _LOG.debug("[%s] ->: %s", websocket.remote_address, data_log)
305
+ _LOG.debug(
306
+ "[%s] ->: %s", websocket.remote_address, filter_log_msg_data(data)
307
+ )
296
308
  await websocket.send(data_dump)
297
309
  else:
298
310
  _LOG.error("Error sending event: connection no longer established")
@@ -469,7 +481,7 @@ class IntegrationAPI:
469
481
  self, websocket, req_id: int, msg_data: dict[str, Any] | None
470
482
  ) -> None:
471
483
  if not msg_data:
472
- _LOG.warning("Ignoring _entity_command: called with empty msg_data")
484
+ _LOG.warning("Ignoring entity command: called with empty msg_data")
473
485
  await self.acknowledge_command(
474
486
  websocket, req_id, uc.StatusCodes.BAD_REQUEST
475
487
  )
@@ -635,6 +647,7 @@ class IntegrationAPI:
635
647
  websocket, uc.WsMsgEvents.DRIVER_SETUP_CHANGE, data, uc.EventCategory.DEVICE
636
648
  )
637
649
 
650
+ # pylint: disable=R0917
638
651
  async def request_driver_setup_user_confirmation(
639
652
  self,
640
653
  websocket,
@@ -852,27 +865,42 @@ def local_hostname() -> str:
852
865
  )
853
866
 
854
867
 
855
- def filter_log_msg_data(data: dict[str, Any]) -> bool:
868
+ def filter_log_msg_data(data: dict[str, Any]) -> dict[str, Any]:
856
869
  """
857
870
  Filter attribute fields to exclude for log messages in the given msg data dict.
858
871
 
859
- Attention: the dictionary is modified!
860
-
861
- - Attributes are filtered in `data["msg_data"]["attributes"]`
872
+ - Attributes are filtered in `data["msg_data"]`:
873
+ - dict object: key `attributes`
874
+ - list object: every list item `attributes`
862
875
  - Filtered attributes: `MEDIA_IMAGE_URL`
863
876
 
864
877
  :param data: the message data dict
865
- :return: True if a field was filtered, False otherwise
878
+ :return: copy of the message data dict with filtered attributes
866
879
  """
880
+ # do not modify the original dict
881
+ log_upd = deepcopy(data)
882
+ if not log_upd:
883
+ return {}
884
+
867
885
  # filter out base64 encoded images in the media player's media_image_url attribute
868
- if (
869
- "msg_data" in data
870
- and "attributes" in data["msg_data"]
871
- and media_player.Attributes.MEDIA_IMAGE_URL in data["msg_data"]["attributes"]
872
- and data["msg_data"]["attributes"][
873
- media_player.Attributes.MEDIA_IMAGE_URL
874
- ].startswith("data:")
875
- ):
876
- data["msg_data"]["attributes"][media_player.Attributes.MEDIA_IMAGE_URL] = "***"
877
- return True
878
- return False
886
+ if "msg_data" in log_upd:
887
+ if (
888
+ "attributes" in log_upd["msg_data"]
889
+ 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:"
892
+ )
893
+ ):
894
+ log_upd["msg_data"]["attributes"][MediaAttr.MEDIA_IMAGE_URL] = "data:***"
895
+ elif isinstance(log_upd["msg_data"], list):
896
+ for item in log_upd["msg_data"]:
897
+ if (
898
+ "attributes" in item
899
+ and MediaAttr.MEDIA_IMAGE_URL in item["attributes"]
900
+ and item["attributes"][MediaAttr.MEDIA_IMAGE_URL].startswith(
901
+ "data:"
902
+ )
903
+ ):
904
+ item["attributes"][MediaAttr.MEDIA_IMAGE_URL] = "data:***"
905
+
906
+ return log_upd
@@ -79,8 +79,9 @@ class Climate(Entity):
79
79
 
80
80
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_climate.md
81
81
  for more information.
82
- """ # noqa
82
+ """
83
83
 
84
+ # pylint: disable=R0917
84
85
  def __init__(
85
86
  self,
86
87
  identifier: str,
@@ -78,8 +78,9 @@ class Cover(Entity):
78
78
 
79
79
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_cover.md
80
80
  for more information.
81
- """ # noqa
81
+ """
82
82
 
83
+ # pylint: disable=R0917
83
84
  def __init__(
84
85
  self,
85
86
  identifier: str,
@@ -36,6 +36,7 @@ class Entity:
36
36
  for more information.
37
37
  """
38
38
 
39
+ # pylint: disable=R0917
39
40
  def __init__(
40
41
  self,
41
42
  identifier: str,
@@ -65,8 +65,9 @@ class Light(Entity):
65
65
 
66
66
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_light.md
67
67
  for more information.
68
- """ # noqa
68
+ """
69
69
 
70
+ # pylint: disable=R0917
70
71
  def __init__(
71
72
  self,
72
73
  identifier: str,
@@ -78,6 +78,7 @@ class Attributes(str, Enum):
78
78
  MUTED = "muted"
79
79
  MEDIA_DURATION = "media_duration"
80
80
  MEDIA_POSITION = "media_position"
81
+ MEDIA_POSITION_UPDATED_AT = "media_position_updated_at"
81
82
  MEDIA_TYPE = "media_type"
82
83
  MEDIA_IMAGE_URL = "media_image_url"
83
84
  MEDIA_TITLE = "media_title"
@@ -193,8 +194,9 @@ class MediaPlayer(Entity):
193
194
 
194
195
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_media_player.md
195
196
  for more information.
196
- """ # noqa
197
+ """
197
198
 
199
+ # pylint: disable=R0917
198
200
  def __init__(
199
201
  self,
200
202
  identifier: str,
@@ -119,8 +119,9 @@ class Remote(Entity):
119
119
 
120
120
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_remote.md
121
121
  for more information.
122
- """ # noqa
122
+ """
123
123
 
124
+ # pylint: disable=R0917
124
125
  def __init__(
125
126
  self,
126
127
  identifier: str,
@@ -64,8 +64,9 @@ class Sensor(Entity):
64
64
 
65
65
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_sensor.md
66
66
  for more information.
67
- """ # noqa
67
+ """
68
68
 
69
+ # pylint: disable=R0917
69
70
  def __init__(
70
71
  self,
71
72
  identifier: str,
@@ -61,8 +61,9 @@ class Switch(Entity):
61
61
 
62
62
  See https://github.com/unfoldedcircle/core-api/blob/main/doc/entities/entity_switch.md
63
63
  for more information.
64
- """ # noqa
64
+ """
65
65
 
66
+ # pylint: disable=R0917
66
67
  def __init__(
67
68
  self,
68
69
  identifier: str,
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ucapi
3
- Version: 0.2.0
3
+ Version: 0.3.1
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
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://www.unfoldedcircle.com/
8
8
  Project-URL: Source Code, https://github.com/unfoldedcircle/integration-python-library
9
9
  Project-URL: Bug Reports, https://github.com/unfoldedcircle/integration-python-library/issues
10
10
  Project-URL: Discord, http://unfolded.chat/
11
- Project-URL: Forum, http://unfolded.community/
11
+ Project-URL: Forum, https://unfolded.community/
12
12
  Platform: any
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Intended Audience :: Developers
@@ -22,7 +22,7 @@ Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown; charset=UTF-8
23
23
  License-File: LICENSE
24
24
  Requires-Dist: pyee>=9.0
25
- Requires-Dist: websockets>=11.0
25
+ Requires-Dist: websockets>=14.0
26
26
  Requires-Dist: zeroconf>=0.120.0
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pylint; extra == "testing"
@@ -30,13 +30,14 @@ Requires-Dist: flake8-docstrings; extra == "testing"
30
30
  Requires-Dist: flake8; extra == "testing"
31
31
  Requires-Dist: black; extra == "testing"
32
32
  Requires-Dist: isort; extra == "testing"
33
+ Dynamic: license-file
33
34
 
34
35
  # Python API wrapper for the UC Integration API
35
36
  [![PyPi](https://img.shields.io/pypi/v/ucapi.svg)](https://pypi.org/project/ucapi)
36
37
  [![License](https://img.shields.io/github/license/unfoldedcircle/integration-python-library.svg)](LICENSE)
37
38
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
38
39
 
39
- This library simplifies writing Python based integrations for the [Unfolded Circle Remote Two](https://www.unfoldedcircle.com/)
40
+ This library simplifies writing Python-based integrations for the [Unfolded Circle Remote devices](https://www.unfoldedcircle.com/)
40
41
  by wrapping the [WebSocket Integration API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api).
41
42
 
42
43
  It's an alpha release (in our eyes). Breaking changes are to be expected and missing features will be continuously added.
@@ -16,6 +16,7 @@ examples/remote.py
16
16
  examples/remote_ui_page.json
17
17
  examples/setup_flow.json
18
18
  examples/setup_flow.py
19
+ tests/test_api.py
19
20
  ucapi/__init__.py
20
21
  ucapi/_version.py
21
22
  ucapi/api.py
@@ -1,5 +1,5 @@
1
1
  pyee>=9.0
2
- websockets>=11.0
2
+ websockets>=14.0
3
3
  zeroconf>=0.120.0
4
4
 
5
5
  [testing]
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