Skip to content

Commit f10e1de

Browse files
authored
feat: Add voice-assistant entity and audio-stream support (#38)
* voice_assistant entity and voice stream handler * signal voice session termination reason to client - For abnormal terminations (timeout, remote abort, local abort, error), raise specific exceptions from the iterator. - expose metadata about the end (reason, error) via properties for post- loop inspection. - Exception hierarchy: - `VoiceSessionClosed(Exception)` base with optional fields (reason, error). - `VoiceSessionTimeout(VoiceSessionClosed)` - `VoiceSessionRemoteEnd(VoiceSessionClosed)` - `VoiceSessionLocalEnd(VoiceSessionClosed)` - `VoiceSessionError(VoiceSessionClosed)` - End reason type: - `VoiceEndReason` Enum: `NORMAL`, `TIMEOUT`, `REMOTE`, `LOCAL`, `ERROR` * Enhance entity command handler with client connection Pass the WS connection as an optional command parameter to the client. This allows the client to send directed WS events back. Otherwise, the only option is to use event broadcasts to all connected clients. Implementation is backward compatible to existing clients without the new websocket parameter in the CommandHandler callback, or extending a concrete Entity class. * Remove excessive logging in Entities.get method * Import cleanup to avoid circular imports through the ucapi definition. * Update readme
1 parent ca38361 commit f10e1de

37 files changed

+1870
-98
lines changed

.idea/misc.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Check_isort.xml

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Run_isort.xml

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ 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+
- Enhance entity command handler with WS client connection parameter in `CommandHandler` callback and `Entity.command`
14+
method to allow clients to send back event messages.
15+
- The implementation is currently backward-compatible but will be removed in a future release.
16+
17+
### Added
18+
- New voice-assistant entity with voice-stream session handling.
19+
20+
### Changed
21+
- Remove logging in Entities.get method if entity doesn't exist. This could lead to excessive logging in some integrations.
22+
1223
---
1324

1425
## v0.4.0 - 2025-11-24

README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,30 @@
44
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
55

66
This library simplifies writing Python-based integrations for the [Unfolded Circle Remote devices](https://www.unfoldedcircle.com/)
7-
by wrapping the [WebSocket Integration API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api).
7+
by wrapping the [WebSocket Integration API](https://github.com/unfoldedcircle/core-api/tree/main/integration-api)
8+
and supporting the [available entities](https://unfoldedcircle.github.io/core-api/entities/).
89

9-
It's an alpha release (in our eyes). Breaking changes are to be expected and missing features will be continuously added.
10-
Based on our [Node.js integration library](https://github.com/unfoldedcircle/integration-node-library).
10+
> [!NOTE]
11+
> Please note that this library is more of a convenience Python wrapper for the WebSocket Integreation-API than a
12+
> full-featured SDK. It is based on our [Node.js integration library](https://github.com/unfoldedcircle/integration-node-library).
1113
12-
❗️**Attention:**
14+
> [!IMPORTANT]
1315
> This is our first Python project, and we don't see ourselves as Python professionals.
1416
> Therefore, the library is most likely not yet that Pythonic!
1517
> We are still learning and value your feedback on how to improve it :-)
1618
1719
Not yet supported:
1820

1921
- Secure WebSocket
20-
- Token based authentication
22+
- Token-based authentication
2123

2224
Requirements:
23-
- Python 3.10 or newer
25+
- Python 3.11 or newer
26+
27+
Integrations using this library:
28+
- [Android TV integration](https://github.com/unfoldedcircle/integration-androidtv)
29+
- [Apple TV integration](https://github.com/unfoldedcircle/integration-appletv)
30+
- [Denon AVR integration](https://github.com/unfoldedcircle/integration-denonavr)
2431

2532
## Installation
2633

@@ -29,8 +36,7 @@ Use pip:
2936
pip3 install ucapi
3037
```
3138

32-
See [examples directory](https://github.com/aitatoi/integration-python-library/blob/main/examples) for a minimal
33-
integration driver example. More examples will be published.
39+
See [examples directory](https://github.com/aitatoi/integration-python-library/blob/main/examples) for some minimal integration driver examples.
3440

3541
### Environment Variables
3642

docs/setup.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,28 @@ Local installation:
1616
```shell
1717
pip3 install --force-reinstall dist/ucapi-$VERSION-py3-none-any.whl
1818
```
19+
20+
## Protobuf
21+
22+
1. Optional (recommended): install the Python plugin toolchain for consistent results:
23+
```bash
24+
python3 -m pip install --upgrade grpcio-tools protobuf
25+
```
26+
2. From the project root, run:
27+
```bash
28+
python3 scripts/compile_protos.py
29+
```
30+
- This will generate `ucapi/proto/ucr_integration_voice_pb2.py` (and `.pyi` if supported).
31+
3. Add and commit the generated files to Git:
32+
```bash
33+
git add ucapi/proto/ucr_integration_voice_pb2.py ucapi/proto/ucr_integration_voice_pb2.pyi || true
34+
git commit -m "Generate protobuf Python modules for voice integration"
35+
```
36+
37+
Notes:
38+
- The library does not re-generate at build time; we ship the generated code with the package.
39+
- If you prefer using system `protoc`, ensure it’s on `PATH`; the script will fall back to it automatically.
40+
- Imports at runtime (if/when needed) will look like:
41+
```python
42+
from ucapi.proto import ucr_integration_voice_pb2 as voice_pb2
43+
```

examples/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# API wrapper examples
22

3-
This directory contains a few examples on how to use the Remote Two Integration-API wrapper.
3+
This directory contains a few examples on how to use the Remote Two/3 Integration-API wrapper.
44

55
Each example uses a driver metadata definition file. It's a json file named after the example.
66
The most important fields are:
@@ -39,3 +39,9 @@ and are not yet available as typed Python objects.
3939

4040
See `Setting` object definition and the referenced SettingTypeNumber, SettingTypeText, SettingTypeTextArea,
4141
SettingTypePassword, SettingTypeCheckbox, SettingTypeDropdown, SettingTypeLabel.
42+
43+
## voice
44+
45+
The [voice ](voice.py) example shows how to use the voice-assistant entity and receiving a microphone audio stream.
46+
47+
Firmware version 2.8.2 or higher is required.

examples/hello_integration.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212

1313
async def cmd_handler(
14-
entity: ucapi.Button, cmd_id: str, _params: dict[str, Any] | None
14+
entity: ucapi.Button, cmd_id: str, _params: dict[str, Any] | None, websocket: Any
1515
) -> ucapi.StatusCodes:
1616
"""
1717
Push button command handler.
@@ -21,6 +21,7 @@ async def cmd_handler(
2121
:param entity: button entity
2222
:param cmd_id: command
2323
:param _params: optional command parameters
24+
:param websocket: optional client connection for sending directed events
2425
:return: status of the command
2526
"""
2627
print(f"Got {entity.id} command request: {cmd_id}")

examples/remote.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747

4848
async def cmd_handler(
49-
entity: ucapi.Remote, cmd_id: str, params: dict[str, Any] | None
49+
entity: ucapi.Remote, cmd_id: str, params: dict[str, Any] | None, websocket: Any
5050
) -> ucapi.StatusCodes:
5151
"""
5252
Remote command handler.
@@ -56,6 +56,7 @@ async def cmd_handler(
5656
:param entity: remote entity
5757
:param cmd_id: command
5858
:param params: optional command parameters
59+
:param websocket: optional client connection for sending directed events
5960
:return: status of the command
6061
"""
6162
print(f"Got {entity.id} command request: {cmd_id}")

examples/setup_flow.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async def handle_driver_setup(
3838
"""
3939
Start driver setup.
4040
41-
Initiated by Remote Two to set up the driver.
41+
Initiated by Remote Two/3 to set up the driver.
4242
4343
:param msg: value(s) of input fields in the first setup screen.
4444
:return: the setup action on how to continue
@@ -157,7 +157,7 @@ async def handle_user_data_response(msg: ucapi.UserDataResponse) -> ucapi.SetupA
157157

158158

159159
async def cmd_handler(
160-
entity: ucapi.Button, cmd_id: str, _params: dict[str, Any] | None
160+
entity: ucapi.Button, cmd_id: str, _params: dict[str, Any] | None, websocket: Any
161161
) -> ucapi.StatusCodes:
162162
"""
163163
Push button command handler.
@@ -167,6 +167,7 @@ async def cmd_handler(
167167
:param entity: button entity
168168
:param cmd_id: command
169169
:param _params: optional command parameters
170+
:param websocket: optional client connection for sending directed events
170171
:return: status of the command
171172
"""
172173
print(f"Got {entity.id} command request: {cmd_id}")

0 commit comments

Comments
 (0)