cresnextws 0.1.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.
- cresnextws-0.1.0/PKG-INFO +564 -0
- cresnextws-0.1.0/README.md +540 -0
- cresnextws-0.1.0/cresnextws/__init__.py +37 -0
- cresnextws-0.1.0/cresnextws/client.py +919 -0
- cresnextws-0.1.0/cresnextws/data_event_manager.py +419 -0
- cresnextws-0.1.0/cresnextws.egg-info/PKG-INFO +564 -0
- cresnextws-0.1.0/cresnextws.egg-info/SOURCES.txt +10 -0
- cresnextws-0.1.0/cresnextws.egg-info/dependency_links.txt +1 -0
- cresnextws-0.1.0/cresnextws.egg-info/requires.txt +9 -0
- cresnextws-0.1.0/cresnextws.egg-info/top_level.txt +1 -0
- cresnextws-0.1.0/pyproject.toml +66 -0
- cresnextws-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cresnextws
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Crestron CresNext WebSocket API Client
|
|
5
|
+
Author: Scott Shanafelt
|
|
6
|
+
Project-URL: Homepage, https://github.com/jetsoncontrols/cresnextws
|
|
7
|
+
Project-URL: Bug Reports, https://github.com/jetsoncontrols/cresnextws/issues
|
|
8
|
+
Project-URL: Source, https://github.com/jetsoncontrols/cresnextws
|
|
9
|
+
Keywords: crestron,cresnext,websocket,api,client
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: System :: Hardware :: Hardware Drivers
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
|
|
25
|
+
# cresnextws
|
|
26
|
+
|
|
27
|
+
Crestron CresNext WebSocket API Client
|
|
28
|
+
|
|
29
|
+
A Python library for interacting with Crestron CresNext systems via WebSocket API.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Install from PyPI (when published):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install cresnextws
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or install from source:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/jetsoncontrols/cresnextws.git
|
|
43
|
+
cd cresnextws
|
|
44
|
+
pip install .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### Basic HTTP Operations
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
import asyncio
|
|
53
|
+
from cresnextws import CresNextWSClient, ClientConfig
|
|
54
|
+
|
|
55
|
+
async def main():
|
|
56
|
+
# Create configuration (required)
|
|
57
|
+
config = ClientConfig(
|
|
58
|
+
host="your-cresnext-host.local",
|
|
59
|
+
username="your_username",
|
|
60
|
+
password="your_password",
|
|
61
|
+
auto_reconnect=True # Enable automatic reconnection
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Create client instance with config
|
|
65
|
+
client = CresNextWSClient(config)
|
|
66
|
+
|
|
67
|
+
# Connect to the system
|
|
68
|
+
await client.connect()
|
|
69
|
+
|
|
70
|
+
# HTTP GET request
|
|
71
|
+
response = await client.http_get("/Device/Ethernet/HostName")
|
|
72
|
+
print(f"Hostname: {response}")
|
|
73
|
+
|
|
74
|
+
# HTTP POST request (update configuration)
|
|
75
|
+
data = {"Device": {"Ethernet": {"HostName": "new-hostname"}}}
|
|
76
|
+
response = await client.http_post("/Device/Ethernet/HostName", data)
|
|
77
|
+
print(f"Update response: {response}")
|
|
78
|
+
|
|
79
|
+
# Disconnect when done
|
|
80
|
+
await client.disconnect()
|
|
81
|
+
|
|
82
|
+
# Run the example
|
|
83
|
+
asyncio.run(main())
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Health Check Configuration
|
|
87
|
+
|
|
88
|
+
The library includes a health check mechanism to detect stale connections (particularly after system sleep/wake cycles) and automatically trigger reconnection:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import asyncio
|
|
92
|
+
from cresnextws import CresNextWSClient, ClientConfig
|
|
93
|
+
|
|
94
|
+
async def main():
|
|
95
|
+
config = ClientConfig(
|
|
96
|
+
host="your-cresnext-host.local",
|
|
97
|
+
username="your_username",
|
|
98
|
+
password="your_password",
|
|
99
|
+
auto_reconnect=True, # Enable automatic reconnection (required for health check)
|
|
100
|
+
health_check_interval=30.0, # Check connection health every 30 seconds (default)
|
|
101
|
+
health_check_timeout=5.0 # Health check ping timeout in seconds (default)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
client = CresNextWSClient(config)
|
|
105
|
+
await client.connect()
|
|
106
|
+
|
|
107
|
+
# Health check runs automatically in the background
|
|
108
|
+
# If a ping fails or times out, it will trigger reconnection
|
|
109
|
+
|
|
110
|
+
# Your application logic here...
|
|
111
|
+
await asyncio.sleep(300) # Run for 5 minutes
|
|
112
|
+
|
|
113
|
+
await client.disconnect()
|
|
114
|
+
|
|
115
|
+
asyncio.run(main())
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Health Check Features:**
|
|
119
|
+
- **Automatic Detection**: Detects stale WebSocket connections after system sleep/wake cycles
|
|
120
|
+
- **Configurable Intervals**: Customize how often to check connection health
|
|
121
|
+
- **Timeout Handling**: Configurable timeout for ping responses
|
|
122
|
+
- **Seamless Integration**: Works alongside existing reconnection system
|
|
123
|
+
- **Zero Configuration**: Enabled by default with sensible defaults when `auto_reconnect=True`
|
|
124
|
+
|
|
125
|
+
**Note**: Health check only runs when `auto_reconnect=True`. If auto-reconnection is disabled, health checks are automatically disabled as well.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### WebSocket Operations
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import asyncio
|
|
132
|
+
from cresnextws import CresNextWSClient, ClientConfig
|
|
133
|
+
|
|
134
|
+
async def main():
|
|
135
|
+
config = ClientConfig(
|
|
136
|
+
host="your-cresnext-host.local",
|
|
137
|
+
username="your_username",
|
|
138
|
+
password="your_password",
|
|
139
|
+
health_check_interval=30.0, # Ping every 30 seconds (default)
|
|
140
|
+
health_check_timeout=5.0 # 5 second ping timeout (default)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
async with CresNextWSClient(config) as client:
|
|
144
|
+
# WebSocket GET - subscribe to data updates
|
|
145
|
+
await client.ws_get("/Device/DeviceInfo/Model")
|
|
146
|
+
|
|
147
|
+
# WebSocket POST - send configuration updates
|
|
148
|
+
data = {"Device": {"Config": {"SomeValue": "new_value"}}}
|
|
149
|
+
await client.ws_post(data)
|
|
150
|
+
|
|
151
|
+
# Listen for incoming messages
|
|
152
|
+
message = await client.next_message(timeout=5.0)
|
|
153
|
+
print(f"Received: {message}")
|
|
154
|
+
|
|
155
|
+
asyncio.run(main())
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Connection Status Events
|
|
159
|
+
|
|
160
|
+
Monitor connection state changes with event callbacks:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import asyncio
|
|
164
|
+
from cresnextws import CresNextWSClient, ClientConfig, ConnectionStatus
|
|
165
|
+
|
|
166
|
+
def on_status_change(status: ConnectionStatus):
|
|
167
|
+
if status == ConnectionStatus.CONNECTED:
|
|
168
|
+
print("🟢 Connected to device!")
|
|
169
|
+
elif status == ConnectionStatus.DISCONNECTED:
|
|
170
|
+
print("🔴 Disconnected from device")
|
|
171
|
+
elif status == ConnectionStatus.CONNECTING:
|
|
172
|
+
print("🟡 Connecting...")
|
|
173
|
+
elif status == ConnectionStatus.RECONNECTING:
|
|
174
|
+
print("🟠 Reconnecting...")
|
|
175
|
+
|
|
176
|
+
async def main():
|
|
177
|
+
config = ClientConfig(
|
|
178
|
+
host="your-cresnext-host.local",
|
|
179
|
+
username="your_username",
|
|
180
|
+
password="your_password",
|
|
181
|
+
auto_reconnect=True
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
client = CresNextWSClient(config)
|
|
185
|
+
|
|
186
|
+
# Subscribe to connection status events
|
|
187
|
+
client.add_connection_status_handler(on_status_change)
|
|
188
|
+
|
|
189
|
+
# Get current status
|
|
190
|
+
print(f"Current status: {client.get_connection_status()}")
|
|
191
|
+
|
|
192
|
+
# Connect (will trigger status events)
|
|
193
|
+
await client.connect()
|
|
194
|
+
|
|
195
|
+
# Your application logic here...
|
|
196
|
+
|
|
197
|
+
# Cleanup
|
|
198
|
+
client.remove_connection_status_handler(on_status_change)
|
|
199
|
+
await client.disconnect()
|
|
200
|
+
|
|
201
|
+
asyncio.run(main())
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Configuration and Utilities
|
|
205
|
+
|
|
206
|
+
Access configuration details and utility methods:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
import asyncio
|
|
210
|
+
from cresnextws import CresNextWSClient, ClientConfig
|
|
211
|
+
|
|
212
|
+
async def main():
|
|
213
|
+
config = ClientConfig(
|
|
214
|
+
host="your-cresnext-host.local",
|
|
215
|
+
username="your_username",
|
|
216
|
+
password="your_password"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
client = CresNextWSClient(config)
|
|
220
|
+
|
|
221
|
+
# Get the base HTTPS endpoint URL
|
|
222
|
+
base_url = client.get_base_endpoint()
|
|
223
|
+
print(f"Base endpoint: {base_url}") # Output: https://your-cresnext-host.local
|
|
224
|
+
|
|
225
|
+
# This is useful for constructing custom URLs or understanding the connection target
|
|
226
|
+
# The base endpoint is used internally for all HTTP requests and WebSocket origins
|
|
227
|
+
|
|
228
|
+
await client.connect()
|
|
229
|
+
# ... your application logic ...
|
|
230
|
+
await client.disconnect()
|
|
231
|
+
|
|
232
|
+
asyncio.run(main())
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### DataEventManager - Real-time Monitoring
|
|
236
|
+
|
|
237
|
+
The `DataEventManager` provides automatic monitoring of WebSocket messages with path-based subscriptions:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
import asyncio
|
|
241
|
+
from cresnextws import CresNextWSClient, ClientConfig, DataEventManager
|
|
242
|
+
|
|
243
|
+
async def main():
|
|
244
|
+
config = ClientConfig(
|
|
245
|
+
host="your-cresnext-host.local",
|
|
246
|
+
username="your_username",
|
|
247
|
+
password="your_password"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
client = CresNextWSClient(config)
|
|
251
|
+
await client.connect()
|
|
252
|
+
|
|
253
|
+
# Create data event manager
|
|
254
|
+
data_manager = DataEventManager(client)
|
|
255
|
+
|
|
256
|
+
# Define callback function
|
|
257
|
+
def on_device_update(path: str, data):
|
|
258
|
+
print(f"Device updated: {path} = {data}")
|
|
259
|
+
|
|
260
|
+
def on_network_change(path: str, data):
|
|
261
|
+
print(f"Network change: {path} = {data}")
|
|
262
|
+
|
|
263
|
+
# Subscribe to different data paths
|
|
264
|
+
data_manager.subscribe("/Device/DeviceInfo/*", on_device_update)
|
|
265
|
+
data_manager.subscribe("/Device/Network/*", on_network_change)
|
|
266
|
+
|
|
267
|
+
# Start monitoring
|
|
268
|
+
await data_manager.start_monitoring()
|
|
269
|
+
|
|
270
|
+
# Request data to trigger callbacks
|
|
271
|
+
await client.ws_get("/Device/DeviceInfo/Model")
|
|
272
|
+
await client.ws_get("/Device/Network/Interface")
|
|
273
|
+
|
|
274
|
+
# Monitor for 30 seconds
|
|
275
|
+
await asyncio.sleep(30)
|
|
276
|
+
|
|
277
|
+
# Clean up
|
|
278
|
+
await data_manager.stop_monitoring()
|
|
279
|
+
await client.disconnect()
|
|
280
|
+
|
|
281
|
+
asyncio.run(main())
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Path Pattern Matching
|
|
285
|
+
|
|
286
|
+
The DataEventManager supports flexible path matching:
|
|
287
|
+
|
|
288
|
+
- **Exact match**: `/Device/Config` - matches only that specific path
|
|
289
|
+
- **Wildcard match**: `/Device/*` - matches any direct child of `/Device/`
|
|
290
|
+
- **Child matching**: `/Device/Config` with `match_children=True` - matches the path and all sub-paths
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
# Examples of path patterns
|
|
294
|
+
data_manager.subscribe("/Device/Config", callback) # Exact match
|
|
295
|
+
data_manager.subscribe("/Device/*", callback) # Wildcard
|
|
296
|
+
data_manager.subscribe("/Device/Config", callback, match_children=True) # Include children
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Full Message Access
|
|
300
|
+
|
|
301
|
+
By default, callbacks receive only the changed value. Use `full_message=True` to access the complete WebSocket message including metadata:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
def value_only_callback(path: str, data):
|
|
305
|
+
print(f"Value: {data}") # Only the changed data
|
|
306
|
+
|
|
307
|
+
def full_message_callback(path: str, message):
|
|
308
|
+
print(f"Full message: {message}") # Complete JSON with timestamps, etc.
|
|
309
|
+
|
|
310
|
+
# Traditional behavior (default)
|
|
311
|
+
data_manager.subscribe("/Device/Config", value_only_callback, full_message=False)
|
|
312
|
+
|
|
313
|
+
# New: Access full message including metadata
|
|
314
|
+
data_manager.subscribe("/Device/Config", full_message_callback, full_message=True)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
#### Context Manager Usage
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
async def monitor_with_context():
|
|
321
|
+
config = ClientConfig(host="your-host.local", username="admin", password="password")
|
|
322
|
+
|
|
323
|
+
async with CresNextWSClient(config) as client:
|
|
324
|
+
async with DataEventManager(client) as data_manager:
|
|
325
|
+
# Add subscriptions
|
|
326
|
+
data_manager.subscribe("/Device/*", lambda path, data: print(f"{path}: {data}"))
|
|
327
|
+
|
|
328
|
+
# Request data
|
|
329
|
+
await client.ws_get("/Device/Info")
|
|
330
|
+
|
|
331
|
+
# Monitor for a while
|
|
332
|
+
await asyncio.sleep(10)
|
|
333
|
+
|
|
334
|
+
# Automatic cleanup when exiting context
|
|
335
|
+
|
|
336
|
+
asyncio.run(monitor_with_context())
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## API Reference
|
|
340
|
+
|
|
341
|
+
### CresNextWSClient Methods
|
|
342
|
+
|
|
343
|
+
#### Connection Management
|
|
344
|
+
- `await client.connect()` - Connect to the CresNext system
|
|
345
|
+
- `await client.disconnect()` - Disconnect from the system
|
|
346
|
+
- `client.connected` - Check connection status
|
|
347
|
+
|
|
348
|
+
#### HTTP Operations
|
|
349
|
+
- `await client.http_get(path)` - Send HTTP GET request
|
|
350
|
+
- `await client.http_post(path, data)` - Send HTTP POST request with JSON data
|
|
351
|
+
|
|
352
|
+
#### WebSocket Operations
|
|
353
|
+
- `await client.ws_get(path)` - Subscribe to WebSocket data updates for a path
|
|
354
|
+
- `await client.ws_post(data)` - Send data via WebSocket
|
|
355
|
+
- `await client.next_message(timeout=None)` - Get next WebSocket message
|
|
356
|
+
|
|
357
|
+
### DataEventManager Methods
|
|
358
|
+
|
|
359
|
+
#### Subscription Management
|
|
360
|
+
- `subscribe(path_pattern, callback, match_children=True, full_message=False)` - Add subscription
|
|
361
|
+
- `full_message=True` - Pass complete JSON message to callback (includes metadata)
|
|
362
|
+
- `full_message=False` - Pass only the changed value to callback (default behavior)
|
|
363
|
+
- `unsubscribe(subscription_id)` - Remove subscription
|
|
364
|
+
- `clear_subscriptions()` - Remove all subscriptions
|
|
365
|
+
- `get_subscriptions()` - List current subscriptions
|
|
366
|
+
|
|
367
|
+
#### Monitoring Control
|
|
368
|
+
- `await start_monitoring()` - Begin monitoring WebSocket messages
|
|
369
|
+
- `await stop_monitoring()` - Stop monitoring
|
|
370
|
+
|
|
371
|
+
### Common API Paths
|
|
372
|
+
|
|
373
|
+
Based on integration testing, common device paths include:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
# Device Information
|
|
377
|
+
"/Device/DeviceInfo/Model"
|
|
378
|
+
"/Device/DeviceInfo/SerialNumber"
|
|
379
|
+
"/Device/DeviceInfo/FirmwareVersion"
|
|
380
|
+
|
|
381
|
+
# Network Configuration
|
|
382
|
+
"/Device/Ethernet/HostName"
|
|
383
|
+
"/Device/Ethernet/IPAddress"
|
|
384
|
+
"/Device/Ethernet/MACAddress"
|
|
385
|
+
|
|
386
|
+
# Device Configuration
|
|
387
|
+
"/Device/Config/*"
|
|
388
|
+
"/Device/Network/*"
|
|
389
|
+
"/Device/State/*"
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Examples
|
|
393
|
+
|
|
394
|
+
For comprehensive examples, see `examples.py` in the repository:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
python3 examples.py
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The examples demonstrate:
|
|
401
|
+
- Basic HTTP and WebSocket operations
|
|
402
|
+
- DataEventManager usage with subscriptions
|
|
403
|
+
- Context manager patterns
|
|
404
|
+
- Error handling and cleanup
|
|
405
|
+
- Batch operations
|
|
406
|
+
- Real-time device monitoring
|
|
407
|
+
|
|
408
|
+
## Development
|
|
409
|
+
|
|
410
|
+
### Setup Development Environment
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
git clone https://github.com/jetsoncontrols/cresnextws.git
|
|
414
|
+
cd cresnextws
|
|
415
|
+
pip install -e .[dev]
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Running Tests
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
pytest
|
|
422
|
+
|
|
423
|
+
To run integration tests:
|
|
424
|
+
pytest -m integration --run-integration --systems <systems entries from services.json>
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Service-driven Integration Tests
|
|
428
|
+
|
|
429
|
+
You can provide real system connection details to pytest without hard-coding them in tests.
|
|
430
|
+
|
|
431
|
+
1) Create a services file:
|
|
432
|
+
- Copy `tests/services.example.json` to `tests/services.json`
|
|
433
|
+
- Edit values or set environment variables referenced by `${VARS}`
|
|
434
|
+
|
|
435
|
+
2) Run integration tests by opting in:
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
pytest --run-integration --systems all
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Alternatively, since integration tests are marked with `@pytest.mark.integration` and excluded by default via project config, you can select them explicitly:
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
pytest -m integration --run-integration [other flags]
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Flags and environment variables:
|
|
448
|
+
- `--services-file PATH` or `CRESNEXTWS_SERVICES_FILE=PATH` to point to a JSON file
|
|
449
|
+
- `--systems name1,name2` or `CRESNEXTWS_SYSTEMS=name1,name2` to select systems
|
|
450
|
+
- Use `--systems all` to include all systems with `"enabled": true`
|
|
451
|
+
|
|
452
|
+
Example JSON structure:
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{
|
|
456
|
+
"systems": {
|
|
457
|
+
"local_sim": {
|
|
458
|
+
"enabled": true,
|
|
459
|
+
"host": "test.local",
|
|
460
|
+
"auth": {"username": "${CRESNEXTWS_USER}", "password": "${CRESNEXTWS_PASS}"}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Notes:
|
|
467
|
+
- Integration tests are skipped unless `--run-integration` is supplied.
|
|
468
|
+
- Missing systems or disabled entries are automatically skipped.
|
|
469
|
+
|
|
470
|
+
### Code Formatting
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
black cresnextws/
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Type Checking
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
mypy cresnextws/
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Features
|
|
483
|
+
|
|
484
|
+
- **Async/await support** for non-blocking operations
|
|
485
|
+
- **HTTP and WebSocket APIs** for comprehensive device interaction
|
|
486
|
+
- **DataEventManager** for real-time monitoring with path-based subscriptions
|
|
487
|
+
- **Full message access** option for receiving complete WebSocket messages with metadata
|
|
488
|
+
- **Connection Status Events** for monitoring connect/disconnect states
|
|
489
|
+
- **Context manager support** for automatic connection management
|
|
490
|
+
- **Type hints** for better development experience
|
|
491
|
+
- **Comprehensive logging** support
|
|
492
|
+
- **Automatic reconnection** capabilities with connection health monitoring
|
|
493
|
+
- **Health check mechanism** to detect stale connections after system sleep/wake cycles
|
|
494
|
+
- **Flexible path pattern matching** with wildcard and child path support
|
|
495
|
+
- Easy-to-use API for Crestron CresNext systems
|
|
496
|
+
|
|
497
|
+
## Requirements
|
|
498
|
+
|
|
499
|
+
- Python 3.8 or higher
|
|
500
|
+
- websockets>=11.0
|
|
501
|
+
- aiohttp>=3.8.0
|
|
502
|
+
|
|
503
|
+
## Contributing
|
|
504
|
+
|
|
505
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
506
|
+
|
|
507
|
+
## Publishing
|
|
508
|
+
|
|
509
|
+
This project is automatically published to PyPI using GitHub Actions. The publishing workflow is triggered by:
|
|
510
|
+
|
|
511
|
+
### For Development Releases (Test PyPI)
|
|
512
|
+
- **Pushes to main branch**: Automatically publishes to Test PyPI for testing
|
|
513
|
+
- **Manual workflow dispatch**: Can be triggered manually with option to publish to Test PyPI
|
|
514
|
+
|
|
515
|
+
### For Production Releases (PyPI)
|
|
516
|
+
- **Version tags**: Create and push a version tag (e.g., `v1.0.0`, `v0.2.1`) to trigger a production release to PyPI
|
|
517
|
+
|
|
518
|
+
### Setting up PyPI Credentials
|
|
519
|
+
|
|
520
|
+
To enable automatic publishing, you need to configure the following secrets in your GitHub repository:
|
|
521
|
+
|
|
522
|
+
1. **For Test PyPI publishing** (pushes to main branch):
|
|
523
|
+
- Go to [Test PyPI](https://test.pypi.org/manage/account/), create an API token
|
|
524
|
+
- Add the token as `TEST_PYPI_API_TOKEN` in GitHub repository secrets
|
|
525
|
+
|
|
526
|
+
2. **For PyPI publishing** (version tags):
|
|
527
|
+
- Go to [PyPI](https://pypi.org/manage/account/), create an API token
|
|
528
|
+
- Add the token as `PYPI_API_TOKEN` in GitHub repository secrets
|
|
529
|
+
|
|
530
|
+
### Creating a Release
|
|
531
|
+
|
|
532
|
+
To create a new release:
|
|
533
|
+
|
|
534
|
+
1. Update the version in `pyproject.toml`
|
|
535
|
+
2. Commit your changes
|
|
536
|
+
3. Create and push a tag:
|
|
537
|
+
```bash
|
|
538
|
+
git tag v1.0.0
|
|
539
|
+
git push origin v1.0.0
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
The GitHub Action will automatically:
|
|
543
|
+
- Run tests across multiple Python versions
|
|
544
|
+
- Build the package
|
|
545
|
+
- Publish to PyPI
|
|
546
|
+
- Create a GitHub release with release notes
|
|
547
|
+
|
|
548
|
+
### Manual Testing
|
|
549
|
+
|
|
550
|
+
To test the package build locally before releasing:
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
# Install build tools
|
|
554
|
+
pip install build twine
|
|
555
|
+
|
|
556
|
+
# Build the package
|
|
557
|
+
python -m build --no-isolation
|
|
558
|
+
|
|
559
|
+
# Check the package
|
|
560
|
+
python -m twine check dist/*
|
|
561
|
+
|
|
562
|
+
# Test upload to Test PyPI (optional)
|
|
563
|
+
python -m twine upload --repository testpypi dist/*
|
|
564
|
+
```
|