ucapi-framework 0.1.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.
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: ucapi-framework
3
+ Version: 0.1.1
4
+ Summary: ucapi framework that provides core functionality for building integrations.
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pyee>=13.0.0
8
+ Requires-Dist: ucapi>=0.3.2
9
+ Requires-Dist: aiohttp>=3.9.0
10
+
11
+ [![Tests](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml/badge.svg)](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml)
12
+ [![Discord](https://badgen.net/discord/online-members/zGVYf58)](https://discord.gg/zGVYf58)
13
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy_Me_A_Coffee ☕-FFDD00?logo=buy-me-a-coffee&logoColor=white&labelColor=grey)](https://buymeacoffee.com/jackpowell)
14
+
15
+ # UCAPI Framework
16
+
17
+ A framework for building Unfolded Circle Remote integrations that handles the repetitive parts of integration development so you can focus on what's important.
18
+
19
+ ## What This Solves
20
+
21
+ Building an Unfolded Circle Remote integration typically involves:
22
+ - Writing 200+ lines of setup flow routing logic
23
+ - Manually managing configuration updates and persistence
24
+ - Implementing device lifecycle management (connect/disconnect/reconnect)
25
+ - Wiring up Remote event handlers
26
+ - Managing global state for devices and entities
27
+ - Handling entity registration and state synchronization
28
+
29
+ This framework provides tested implementations of all these patterns, reducing a simple integration from ~1500 lines of boilerplate to ~400 lines of device-specific code. It even adds features, like back and restore, for free.
30
+
31
+ ## Core Features
32
+
33
+ ### Standard Setup Flow with Extension Points
34
+
35
+ The setup flow handles the common pattern: configuration mode → discovery/manual entry → device selection. But every integration has unique needs, so there are extension points at key moments:
36
+
37
+ - **Pre-discovery screens** - Collect API credentials or server addresses before running discovery
38
+ - **Post-selection screens** - Gather device-specific settings after the user picks a device
39
+ - **Custom discovery fields** - Add extra fields to the discovery screen (zones, profiles, etc.)
40
+
41
+ The framework handles all the routing, state management, duplicate checking, and configuration persistence. You just implement the screens you need.
42
+
43
+ **Reduction**: Setup flow code goes from ~200 lines to ~50 lines.
44
+
45
+ ### Device Connection Patterns
46
+
47
+ Four base classes cover the common connection patterns:
48
+
49
+ **StatelessHTTPDevice** - For REST APIs. You implement `verify_connection()` to test reachability. No connection management needed.
50
+
51
+ **PollingDevice** - For devices that need periodic state checks. You set a poll interval and implement `poll_device()`. Automatic reconnection on errors.
52
+
53
+ **WebSocketDevice** - For WebSocket connections. You implement `create_websocket()` and `handle_message()`. Framework manages the connection lifecycle, reconnection, and cleanup.
54
+
55
+ **PersistentConnectionDevice** - For TCP, serial, or custom protocols. You implement `establish_connection()`, `receive_data()`, and `close_connection()`. Framework handles the receive loop and error recovery.
56
+
57
+ All connection management, error handling, reconnection logic, and cleanup happens automatically.
58
+
59
+ **Reduction**: Device implementation goes from ~100 lines of connection boilerplate to ~30 lines of business logic.
60
+
61
+ ### Configuration Management
62
+
63
+ Configuration is just a dataclass. The framework handles JSON serialization, CRUD operations, and persistence:
64
+
65
+ ```python
66
+ @dataclass
67
+ class MyDeviceConfig:
68
+ device_id: str
69
+ name: str
70
+ host: str
71
+
72
+ config = BaseDeviceManager("config.json", MyDeviceConfig)
73
+ ```
74
+
75
+ You get full CRUD operations: `add_or_update()`, `get()`, `remove()`, `all()`, `clear()`. Plus automatic backup/restore functionality for free. The framework handles all the file I/O, error handling, and atomic writes.
76
+
77
+ Full type safety means IDE autocomplete works everywhere. No more dict manipulation or manual JSON handling.
78
+
79
+ **Reduction**: Configuration management goes from ~80 lines to ~15 lines.
80
+
81
+ ### Driver Integration
82
+
83
+ The driver coordinates everything - device lifecycle, entity management, and Remote events. You implement four required methods that define your integration's specifics:
84
+
85
+ - **`device_from_entity_id()`** - Extract device ID from entity ID
86
+ - **`get_entity_ids_for_device()`** - Map device to its entities
87
+ - **`map_device_state()`** - Convert device state to entity state
88
+ - **`create_entities()`** - Instantiate entity objects
89
+
90
+ Everything else is automatic. The framework handles Remote connection events (connect, disconnect, standby), entity subscriptions, device lifecycle management, and state synchronization. You can override the event handlers if needed, but the defaults work for most cases.
91
+
92
+ Device events (like state changes) automatically propagate to entity state updates. The framework maintains the connection between your devices and your remote.
93
+
94
+ **Reduction**: Driver code goes from ~300 lines to ~90 lines.
95
+
96
+ ### Discovery (Optional)
97
+
98
+ If your devices support network discovery, the framework provides implementations for common protocols:
99
+
100
+ **SSDPDiscovery** - For UPnP/SSDP devices. Define your service type and implement `create_discovered_device()` to convert SSDP responses into device configs.
101
+
102
+ **ZeroconfDiscovery** - For mDNS/Bonjour devices. Same pattern: service type + conversion method.
103
+
104
+ **NetworkProbeDiscovery** - For devices that need active probing. Scans local network ranges and calls your `probe_host()` method for each IP.
105
+
106
+ All discovery classes handle the protocol details, timeouts, and error handling. Dependencies are lazy-loaded, so you only install what you use (ssdpy, zeroconf, etc.). If your integration doesn't support discovery, just return an empty list from `discover_devices()` and focus on manual entry.
107
+
108
+ ### Event System
109
+
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
+
112
+ Device events (state changes, errors) automatically propagate to entity state updates. You just emit events from your device and the framework keeps the Remote in sync.
113
+
114
+ ## How It Works
115
+
116
+ You inherit from base classes and implement a few required methods:
117
+
118
+ **Driver** - Map between device states and entity states. Create entity instances.
119
+
120
+ **Device** - Implement your connection pattern (verify, poll, handle messages, etc.).
121
+
122
+ **Setup Flow** - Define how to discover devices and create configurations from user input.
123
+
124
+ **Config** - Just a dataclass.
125
+
126
+ The framework handles everything else: lifecycle management, event routing, state synchronization, configuration persistence, error handling, and reconnection logic.
127
+
128
+ ## Architecture
129
+
130
+ The framework is layered:
131
+
132
+ ```
133
+ Your Integration (device logic, API calls, protocol handling)
134
+
135
+ BaseIntegrationDriver (lifecycle, events, entity management)
136
+
137
+ Device Interfaces (connection patterns, error handling)
138
+
139
+ Setup Flow + Config Manager (user interaction, persistence)
140
+ ```
141
+
142
+ Each layer handles its responsibility and provides clean extension points. You only touch the top layer.
143
+
144
+ ## Generic Type System
145
+
146
+ The framework uses bounded generics (`DeviceT`, `ConfigT`) so your IDE knows exactly what types you're working with:
147
+
148
+ ```python
149
+ class MyDriver(BaseIntegrationDriver[MyDevice, MyDeviceConfig]):
150
+ def get_device(self, device_id: str) -> MyDevice | None:
151
+ device = super().get_device(device_id)
152
+ # IDE knows device is MyDevice, full autocomplete available
153
+ ```
154
+
155
+ No casting, no generic types, just full type safety throughout.
156
+
157
+ ## Discovery Support
158
+
159
+ Optional discovery implementations for common protocols:
160
+
161
+ - **SSDPDiscovery** - For UPnP/SSDP devices
162
+ - **ZeroconfDiscovery** - For mDNS/Bonjour devices
163
+ - **NetworkProbeDiscovery** - For scanning IP ranges
164
+
165
+ Lazy imports mean you only need the dependencies if you use them.
166
+
167
+ ## Real-World Example
168
+
169
+ See the PSN integration in this repository:
170
+
171
+ - `intg-psn/driver.py` - 90 lines (was 300)
172
+ - `intg-psn/psn.py` - 140 lines (was 240)
173
+ - `intg-psn/setup_flow.py` - 50 lines (was 250)
174
+ - `intg-psn/config.py` - 15 lines (was 95)
175
+
176
+ Total: ~295 lines of integration code vs ~885 lines previously. And the new code is type-safe, testable, and maintainable.
177
+
178
+ ## Migration
179
+
180
+ If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples.
181
+
182
+ ## Requirements
183
+
184
+ - Python 3.11+
185
+ - ucapi
186
+ - pyee
187
+
188
+ Optional (only if you use them):
189
+ - aiohttp (for HTTP devices)
190
+ - websockets (for WebSocket devices)
191
+ - ssdpy (for SSDP discovery)
192
+ - zeroconf (for mDNS discovery)
193
+
194
+ ## License
195
+
196
+ Mozilla Public License Version 2.0
@@ -0,0 +1,186 @@
1
+ [![Tests](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml/badge.svg)](https://github.com/jackjpowell/ucapi-framework/actions/workflows/test.yml)
2
+ [![Discord](https://badgen.net/discord/online-members/zGVYf58)](https://discord.gg/zGVYf58)
3
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy_Me_A_Coffee ☕-FFDD00?logo=buy-me-a-coffee&logoColor=white&labelColor=grey)](https://buymeacoffee.com/jackpowell)
4
+
5
+ # UCAPI Framework
6
+
7
+ A framework for building Unfolded Circle Remote integrations that handles the repetitive parts of integration development so you can focus on what's important.
8
+
9
+ ## What This Solves
10
+
11
+ Building an Unfolded Circle Remote integration typically involves:
12
+ - Writing 200+ lines of setup flow routing logic
13
+ - Manually managing configuration updates and persistence
14
+ - Implementing device lifecycle management (connect/disconnect/reconnect)
15
+ - Wiring up Remote event handlers
16
+ - Managing global state for devices and entities
17
+ - Handling entity registration and state synchronization
18
+
19
+ This framework provides tested implementations of all these patterns, reducing a simple integration from ~1500 lines of boilerplate to ~400 lines of device-specific code. It even adds features, like back and restore, for free.
20
+
21
+ ## Core Features
22
+
23
+ ### Standard Setup Flow with Extension Points
24
+
25
+ The setup flow handles the common pattern: configuration mode → discovery/manual entry → device selection. But every integration has unique needs, so there are extension points at key moments:
26
+
27
+ - **Pre-discovery screens** - Collect API credentials or server addresses before running discovery
28
+ - **Post-selection screens** - Gather device-specific settings after the user picks a device
29
+ - **Custom discovery fields** - Add extra fields to the discovery screen (zones, profiles, etc.)
30
+
31
+ The framework handles all the routing, state management, duplicate checking, and configuration persistence. You just implement the screens you need.
32
+
33
+ **Reduction**: Setup flow code goes from ~200 lines to ~50 lines.
34
+
35
+ ### Device Connection Patterns
36
+
37
+ Four base classes cover the common connection patterns:
38
+
39
+ **StatelessHTTPDevice** - For REST APIs. You implement `verify_connection()` to test reachability. No connection management needed.
40
+
41
+ **PollingDevice** - For devices that need periodic state checks. You set a poll interval and implement `poll_device()`. Automatic reconnection on errors.
42
+
43
+ **WebSocketDevice** - For WebSocket connections. You implement `create_websocket()` and `handle_message()`. Framework manages the connection lifecycle, reconnection, and cleanup.
44
+
45
+ **PersistentConnectionDevice** - For TCP, serial, or custom protocols. You implement `establish_connection()`, `receive_data()`, and `close_connection()`. Framework handles the receive loop and error recovery.
46
+
47
+ All connection management, error handling, reconnection logic, and cleanup happens automatically.
48
+
49
+ **Reduction**: Device implementation goes from ~100 lines of connection boilerplate to ~30 lines of business logic.
50
+
51
+ ### Configuration Management
52
+
53
+ Configuration is just a dataclass. The framework handles JSON serialization, CRUD operations, and persistence:
54
+
55
+ ```python
56
+ @dataclass
57
+ class MyDeviceConfig:
58
+ device_id: str
59
+ name: str
60
+ host: str
61
+
62
+ config = BaseDeviceManager("config.json", MyDeviceConfig)
63
+ ```
64
+
65
+ You get full CRUD operations: `add_or_update()`, `get()`, `remove()`, `all()`, `clear()`. Plus automatic backup/restore functionality for free. The framework handles all the file I/O, error handling, and atomic writes.
66
+
67
+ Full type safety means IDE autocomplete works everywhere. No more dict manipulation or manual JSON handling.
68
+
69
+ **Reduction**: Configuration management goes from ~80 lines to ~15 lines.
70
+
71
+ ### Driver Integration
72
+
73
+ The driver coordinates everything - device lifecycle, entity management, and Remote events. You implement four required methods that define your integration's specifics:
74
+
75
+ - **`device_from_entity_id()`** - Extract device ID from entity ID
76
+ - **`get_entity_ids_for_device()`** - Map device to its entities
77
+ - **`map_device_state()`** - Convert device state to entity state
78
+ - **`create_entities()`** - Instantiate entity objects
79
+
80
+ Everything else is automatic. The framework handles Remote connection events (connect, disconnect, standby), entity subscriptions, device lifecycle management, and state synchronization. You can override the event handlers if needed, but the defaults work for most cases.
81
+
82
+ Device events (like state changes) automatically propagate to entity state updates. The framework maintains the connection between your devices and your remote.
83
+
84
+ **Reduction**: Driver code goes from ~300 lines to ~90 lines.
85
+
86
+ ### Discovery (Optional)
87
+
88
+ If your devices support network discovery, the framework provides implementations for common protocols:
89
+
90
+ **SSDPDiscovery** - For UPnP/SSDP devices. Define your service type and implement `create_discovered_device()` to convert SSDP responses into device configs.
91
+
92
+ **ZeroconfDiscovery** - For mDNS/Bonjour devices. Same pattern: service type + conversion method.
93
+
94
+ **NetworkProbeDiscovery** - For devices that need active probing. Scans local network ranges and calls your `probe_host()` method for each IP.
95
+
96
+ All discovery classes handle the protocol details, timeouts, and error handling. Dependencies are lazy-loaded, so you only install what you use (ssdpy, zeroconf, etc.). If your integration doesn't support discovery, just return an empty list from `discover_devices()` and focus on manual entry.
97
+
98
+ ### Event System
99
+
100
+ 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.
101
+
102
+ Device events (state changes, errors) automatically propagate to entity state updates. You just emit events from your device and the framework keeps the Remote in sync.
103
+
104
+ ## How It Works
105
+
106
+ You inherit from base classes and implement a few required methods:
107
+
108
+ **Driver** - Map between device states and entity states. Create entity instances.
109
+
110
+ **Device** - Implement your connection pattern (verify, poll, handle messages, etc.).
111
+
112
+ **Setup Flow** - Define how to discover devices and create configurations from user input.
113
+
114
+ **Config** - Just a dataclass.
115
+
116
+ The framework handles everything else: lifecycle management, event routing, state synchronization, configuration persistence, error handling, and reconnection logic.
117
+
118
+ ## Architecture
119
+
120
+ The framework is layered:
121
+
122
+ ```
123
+ Your Integration (device logic, API calls, protocol handling)
124
+
125
+ BaseIntegrationDriver (lifecycle, events, entity management)
126
+
127
+ Device Interfaces (connection patterns, error handling)
128
+
129
+ Setup Flow + Config Manager (user interaction, persistence)
130
+ ```
131
+
132
+ Each layer handles its responsibility and provides clean extension points. You only touch the top layer.
133
+
134
+ ## Generic Type System
135
+
136
+ The framework uses bounded generics (`DeviceT`, `ConfigT`) so your IDE knows exactly what types you're working with:
137
+
138
+ ```python
139
+ class MyDriver(BaseIntegrationDriver[MyDevice, MyDeviceConfig]):
140
+ def get_device(self, device_id: str) -> MyDevice | None:
141
+ device = super().get_device(device_id)
142
+ # IDE knows device is MyDevice, full autocomplete available
143
+ ```
144
+
145
+ No casting, no generic types, just full type safety throughout.
146
+
147
+ ## Discovery Support
148
+
149
+ Optional discovery implementations for common protocols:
150
+
151
+ - **SSDPDiscovery** - For UPnP/SSDP devices
152
+ - **ZeroconfDiscovery** - For mDNS/Bonjour devices
153
+ - **NetworkProbeDiscovery** - For scanning IP ranges
154
+
155
+ Lazy imports mean you only need the dependencies if you use them.
156
+
157
+ ## Real-World Example
158
+
159
+ See the PSN integration in this repository:
160
+
161
+ - `intg-psn/driver.py` - 90 lines (was 300)
162
+ - `intg-psn/psn.py` - 140 lines (was 240)
163
+ - `intg-psn/setup_flow.py` - 50 lines (was 250)
164
+ - `intg-psn/config.py` - 15 lines (was 95)
165
+
166
+ Total: ~295 lines of integration code vs ~885 lines previously. And the new code is type-safe, testable, and maintainable.
167
+
168
+ ## Migration
169
+
170
+ If you have an existing integration, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) for step-by-step instructions with before/after examples.
171
+
172
+ ## Requirements
173
+
174
+ - Python 3.11+
175
+ - ucapi
176
+ - pyee
177
+
178
+ Optional (only if you use them):
179
+ - aiohttp (for HTTP devices)
180
+ - websockets (for WebSocket devices)
181
+ - ssdpy (for SSDP discovery)
182
+ - zeroconf (for mDNS discovery)
183
+
184
+ ## License
185
+
186
+ Mozilla Public License Version 2.0
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "ucapi-framework"
3
+ version = "0.1.1"
4
+ description = "ucapi framework that provides core functionality for building integrations."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "pyee>=13.0.0",
9
+ "ucapi>=0.3.2",
10
+ "aiohttp>=3.9.0",
11
+ ]
12
+
13
+ [dependency-groups]
14
+ dev = [
15
+ "pytest>=9.0.1",
16
+ "pytest-asyncio>=0.23.0",
17
+ "pytest-cov>=4.1.0",
18
+ ]
19
+
20
+ [tool.pytest.ini_options]
21
+ testpaths = ["tests"]
22
+ python_files = ["test_*.py"]
23
+ python_classes = ["Test*"]
24
+ python_functions = ["test_*"]
25
+ asyncio_mode = "auto"
26
+ addopts = [
27
+ "-v",
28
+ "--tb=short",
29
+ "--strict-markers",
30
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+