ucapi-framework 1.9.0b4__tar.gz → 1.9.2__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_framework-1.9.0b4 → ucapi_framework-1.9.2}/PKG-INFO +20 -27
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/README.md +19 -26
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/pyproject.toml +1 -1
- ucapi_framework-1.9.2/tests/test_media_player_entity.py +538 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_select_entity.py +10 -6
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/__init__.py +3 -1
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/driver.py +5 -5
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/select.py +12 -3
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/helpers.py +163 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework.egg-info/PKG-INFO +20 -27
- ucapi_framework-1.9.0b4/tests/test_media_player_entity.py +0 -259
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/setup.cfg +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_button_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_climate_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_config.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_cover_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_device.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_discovery.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_driver.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_factory_pattern.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_helpers.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_ir_emitter_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_light_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_migration.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_remote_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_sensor_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_setup.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_switch_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/tests/test_voice_assistant_entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/config.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/device.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/discovery.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/__init__.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/button.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/climate.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/cover.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/ir_emitter.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/light.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/media_player.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/remote.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/sensor.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/switch.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entities/voice_assistant.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/entity.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/migration.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework/setup.py +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework.egg-info/SOURCES.txt +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework.egg-info/dependency_links.txt +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework.egg-info/requires.txt +0 -0
- {ucapi_framework-1.9.0b4 → ucapi_framework-1.9.2}/ucapi_framework.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ucapi-framework
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.2
|
|
4
4
|
Summary: ucapi framework that provides core functionality for building integrations.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -10,7 +10,7 @@ Requires-Dist: aiohttp>=3.9.0
|
|
|
10
10
|
|
|
11
11
|
[](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml)
|
|
12
12
|
[](https://discord.gg/zGVYf58)
|
|
13
|
-
[](https://buymeacoffee.com/jackpowell)
|
|
14
14
|
|
|
15
15
|
# UCAPI Framework
|
|
16
16
|
|
|
@@ -86,12 +86,12 @@ The driver coordinates everything - device lifecycle, entity management, and Rem
|
|
|
86
86
|
|
|
87
87
|
The framework provides sensible defaults for:
|
|
88
88
|
|
|
89
|
-
- **`create_entities()`** -
|
|
90
|
-
- **`map_device_state()`** - Maps common state strings (ON, OFF, PLAYING, etc.)
|
|
91
|
-
- **`device_from_entity_id()`** - Parses standard
|
|
89
|
+
- **`create_entities()`** - Instantiates entities from the `entity_classes` list passed to the constructor; each item can be a class or a factory lambda `(config, device) -> Entity | list[Entity]`
|
|
90
|
+
- **`map_device_state()`** - Maps common state strings (ON, OFF, PLAYING, etc.) to `media_player.States`
|
|
91
|
+
- **`device_from_entity_id()`** - Parses the standard `entity_type.device_id` format
|
|
92
92
|
- **`get_entity_ids_for_device()`** - Queries and filters entities by device
|
|
93
93
|
|
|
94
|
-
**Override only what you need**: Custom state enums? Override `map_device_state()`.
|
|
94
|
+
**Override only what you need**: Custom state enums? Override `map_device_state()`. Logic that can't fit in a lambda? Override `create_entities()`. Custom entity ID format? Override `device_from_entity_id()`.
|
|
95
95
|
|
|
96
96
|
Everything else is automatic. The framework handles Remote connection events (connect, disconnect, standby), entity subscriptions, device lifecycle management, and state synchronization.
|
|
97
97
|
|
|
@@ -119,13 +119,13 @@ All discovery classes handle the protocol details, timeouts, and error handling.
|
|
|
119
119
|
|
|
120
120
|
The driver base class automatically wires up Remote events (connect, disconnect, standby, subscribe/unsubscribe) with sensible defaults. You can override any of them, but the defaults handle most cases.
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
State flows from device to Remote via the coordinator pattern: the device stores raw state properties and calls `push_update()` when anything changes. Every entity subscribed via `subscribe_to_device()` has its `sync_state()` method called automatically, which reads from the device and calls `self.update({...})` with a fresh dict. The framework diffs against the last-pushed state and sends only changed attributes to the Remote.
|
|
123
123
|
|
|
124
124
|
## How It Works
|
|
125
125
|
|
|
126
126
|
You inherit from base classes and override only what you need:
|
|
127
127
|
|
|
128
|
-
**Driver** - Usually works with defaults
|
|
128
|
+
**Driver** - Usually works with defaults. Pass `entity_classes` to the constructor. Override only if you need custom state mapping or conditional entity creation.
|
|
129
129
|
|
|
130
130
|
**Device** - Implement your connection pattern (verify, poll, handle messages, etc.).
|
|
131
131
|
|
|
@@ -176,34 +176,22 @@ Optional discovery implementations for common protocols:
|
|
|
176
176
|
|
|
177
177
|
Lazy imports mean you only need the dependencies if you use them.
|
|
178
178
|
|
|
179
|
-
## Real-World Example
|
|
180
|
-
|
|
181
|
-
See the PSN integration in this repository:
|
|
182
|
-
|
|
183
|
-
- `intg-psn/driver.py` - 90 lines (was 300)
|
|
184
|
-
- `intg-psn/psn.py` - 140 lines (was 240)
|
|
185
|
-
- `intg-psn/setup_flow.py` - 50 lines (was 250)
|
|
186
|
-
- `intg-psn/config.py` - 15 lines (was 95)
|
|
187
|
-
|
|
188
|
-
Total: ~295 lines of integration code vs ~885 lines previously. And the new code is type-safe, testable, and maintainable.
|
|
189
179
|
|
|
190
180
|
## Migration
|
|
191
181
|
|
|
192
|
-
If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples
|
|
193
|
-
|
|
194
|
-
For upgrading from a previous version of ucapi-framework:
|
|
195
|
-
- **[Upgrading to 1.6.0](https://jackjpowell.github.io/ucapi-framework/upgrade-to-1.6.0/)** - New dynamic entity management features
|
|
182
|
+
If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples covering the coordinator pattern, `subscribe_to_device`, `sync_state`, and `push_update`.
|
|
196
183
|
|
|
197
184
|
## Requirements
|
|
198
185
|
|
|
199
186
|
- Python 3.11+
|
|
200
187
|
- ucapi
|
|
201
188
|
- pyee
|
|
189
|
+
- aiohttp (required; used by HTTP and WebSocket device base classes)
|
|
190
|
+
|
|
191
|
+
Optional (only if you use the corresponding discovery classes):
|
|
202
192
|
|
|
203
|
-
Optional (only if you use them):
|
|
204
|
-
- aiohttp (for HTTP devices)
|
|
205
|
-
- websockets (for WebSocket devices)
|
|
206
193
|
- ssdpy (for SSDP discovery)
|
|
194
|
+
- sddp-discovery-protocol (for SDDP discovery)
|
|
207
195
|
- zeroconf (for mDNS discovery)
|
|
208
196
|
|
|
209
197
|
## Documentation
|
|
@@ -231,9 +219,14 @@ Visit <http://127.0.0.1:8000> to view the docs.
|
|
|
231
219
|
uv sync --group dev
|
|
232
220
|
```
|
|
233
221
|
|
|
234
|
-
|
|
222
|
+
A `pre-commit` hook is available in the `git-hooks/` directory. To activate it:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
cp git-hooks/pre-commit .git/hooks/pre-commit
|
|
226
|
+
chmod +x .git/hooks/pre-commit
|
|
227
|
+
```
|
|
235
228
|
|
|
236
|
-
|
|
229
|
+
The hook runs `ruff check --fix` and `ruff format` before every commit.
|
|
237
230
|
|
|
238
231
|
All development tools run through `uv` and are configured in `pyproject.toml`.
|
|
239
232
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml)
|
|
2
2
|
[](https://discord.gg/zGVYf58)
|
|
3
|
-
[](https://buymeacoffee.com/jackpowell)
|
|
4
4
|
|
|
5
5
|
# UCAPI Framework
|
|
6
6
|
|
|
@@ -76,12 +76,12 @@ The driver coordinates everything - device lifecycle, entity management, and Rem
|
|
|
76
76
|
|
|
77
77
|
The framework provides sensible defaults for:
|
|
78
78
|
|
|
79
|
-
- **`create_entities()`** -
|
|
80
|
-
- **`map_device_state()`** - Maps common state strings (ON, OFF, PLAYING, etc.)
|
|
81
|
-
- **`device_from_entity_id()`** - Parses standard
|
|
79
|
+
- **`create_entities()`** - Instantiates entities from the `entity_classes` list passed to the constructor; each item can be a class or a factory lambda `(config, device) -> Entity | list[Entity]`
|
|
80
|
+
- **`map_device_state()`** - Maps common state strings (ON, OFF, PLAYING, etc.) to `media_player.States`
|
|
81
|
+
- **`device_from_entity_id()`** - Parses the standard `entity_type.device_id` format
|
|
82
82
|
- **`get_entity_ids_for_device()`** - Queries and filters entities by device
|
|
83
83
|
|
|
84
|
-
**Override only what you need**: Custom state enums? Override `map_device_state()`.
|
|
84
|
+
**Override only what you need**: Custom state enums? Override `map_device_state()`. Logic that can't fit in a lambda? Override `create_entities()`. Custom entity ID format? Override `device_from_entity_id()`.
|
|
85
85
|
|
|
86
86
|
Everything else is automatic. The framework handles Remote connection events (connect, disconnect, standby), entity subscriptions, device lifecycle management, and state synchronization.
|
|
87
87
|
|
|
@@ -109,13 +109,13 @@ All discovery classes handle the protocol details, timeouts, and error handling.
|
|
|
109
109
|
|
|
110
110
|
The driver base class automatically wires up Remote events (connect, disconnect, standby, subscribe/unsubscribe) with sensible defaults. You can override any of them, but the defaults handle most cases.
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
State flows from device to Remote via the coordinator pattern: the device stores raw state properties and calls `push_update()` when anything changes. Every entity subscribed via `subscribe_to_device()` has its `sync_state()` method called automatically, which reads from the device and calls `self.update({...})` with a fresh dict. The framework diffs against the last-pushed state and sends only changed attributes to the Remote.
|
|
113
113
|
|
|
114
114
|
## How It Works
|
|
115
115
|
|
|
116
116
|
You inherit from base classes and override only what you need:
|
|
117
117
|
|
|
118
|
-
**Driver** - Usually works with defaults
|
|
118
|
+
**Driver** - Usually works with defaults. Pass `entity_classes` to the constructor. Override only if you need custom state mapping or conditional entity creation.
|
|
119
119
|
|
|
120
120
|
**Device** - Implement your connection pattern (verify, poll, handle messages, etc.).
|
|
121
121
|
|
|
@@ -166,34 +166,22 @@ Optional discovery implementations for common protocols:
|
|
|
166
166
|
|
|
167
167
|
Lazy imports mean you only need the dependencies if you use them.
|
|
168
168
|
|
|
169
|
-
## Real-World Example
|
|
170
|
-
|
|
171
|
-
See the PSN integration in this repository:
|
|
172
|
-
|
|
173
|
-
- `intg-psn/driver.py` - 90 lines (was 300)
|
|
174
|
-
- `intg-psn/psn.py` - 140 lines (was 240)
|
|
175
|
-
- `intg-psn/setup_flow.py` - 50 lines (was 250)
|
|
176
|
-
- `intg-psn/config.py` - 15 lines (was 95)
|
|
177
|
-
|
|
178
|
-
Total: ~295 lines of integration code vs ~885 lines previously. And the new code is type-safe, testable, and maintainable.
|
|
179
169
|
|
|
180
170
|
## Migration
|
|
181
171
|
|
|
182
|
-
If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples
|
|
183
|
-
|
|
184
|
-
For upgrading from a previous version of ucapi-framework:
|
|
185
|
-
- **[Upgrading to 1.6.0](https://jackjpowell.github.io/ucapi-framework/upgrade-to-1.6.0/)** - New dynamic entity management features
|
|
172
|
+
If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples covering the coordinator pattern, `subscribe_to_device`, `sync_state`, and `push_update`.
|
|
186
173
|
|
|
187
174
|
## Requirements
|
|
188
175
|
|
|
189
176
|
- Python 3.11+
|
|
190
177
|
- ucapi
|
|
191
178
|
- pyee
|
|
179
|
+
- aiohttp (required; used by HTTP and WebSocket device base classes)
|
|
180
|
+
|
|
181
|
+
Optional (only if you use the corresponding discovery classes):
|
|
192
182
|
|
|
193
|
-
Optional (only if you use them):
|
|
194
|
-
- aiohttp (for HTTP devices)
|
|
195
|
-
- websockets (for WebSocket devices)
|
|
196
183
|
- ssdpy (for SSDP discovery)
|
|
184
|
+
- sddp-discovery-protocol (for SDDP discovery)
|
|
197
185
|
- zeroconf (for mDNS discovery)
|
|
198
186
|
|
|
199
187
|
## Documentation
|
|
@@ -221,9 +209,14 @@ Visit <http://127.0.0.1:8000> to view the docs.
|
|
|
221
209
|
uv sync --group dev
|
|
222
210
|
```
|
|
223
211
|
|
|
224
|
-
|
|
212
|
+
A `pre-commit` hook is available in the `git-hooks/` directory. To activate it:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
cp git-hooks/pre-commit .git/hooks/pre-commit
|
|
216
|
+
chmod +x .git/hooks/pre-commit
|
|
217
|
+
```
|
|
225
218
|
|
|
226
|
-
|
|
219
|
+
The hook runs `ruff check --fix` and `ruff format` before every commit.
|
|
227
220
|
|
|
228
221
|
All development tools run through `uv` and are configured in `pyproject.toml`.
|
|
229
222
|
|