Skip to content

Commit 4e643cf

Browse files
committed
feat(client): Add GetCapabilities fallback for service discovery when GetServices is not supported
1 parent 65de793 commit 4e643cf

File tree

4 files changed

+234
-96
lines changed

4 files changed

+234
-96
lines changed

README.md

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ client = ONVIFClient(
313313
## Service Discovery: Understanding Device Capabilities
314314

315315
> [!WARNING]
316-
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically uses `GetServices` during initialization to discover service endpoints, but you can also query services manually for detailed information including capabilities.
316+
> Before performing any operations on an ONVIF device, it is highly recommended to discover which services are available and supported by the device. This library automatically performs comprehensive service discovery during initialization using a robust fallback mechanism.
317317
318318
**Why discover device services?**
319319

@@ -324,50 +324,84 @@ client = ONVIFClient(
324324

325325
**How service discovery works in this library:**
326326

327-
The `ONVIFClient` automatically calls `GetServices` during initialization to build a service map. This map is used internally to resolve service endpoints:
327+
The `ONVIFClient` uses a **3-tier discovery approach** to maximize device compatibility:
328+
329+
1. **GetServices (Preferred)** - Tries `GetServices` first for detailed service information
330+
2. **GetCapabilities (Fallback)** - Falls back to `GetCapabilities` if `GetServices` is not supported
331+
3. **Default URLs (Final Fallback)** - Uses standard ONVIF URLs as last resort
328332

329333
```python
330334
from onvif import ONVIFClient
331335

332336
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
333337

334-
# Access the discovered services
335-
print(client.services)
336-
# Example output: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
337-
338-
# Check the service map (namespace -> XAddr mapping)
339-
print(client._service_map)
340-
# Example output: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
338+
# Check what discovery method was used
339+
if client.services:
340+
print("Service discovery: GetServices (preferred)")
341+
print("Discovered services:", len(client.services))
342+
print("Service map:", client._service_map)
343+
elif client.capabilities:
344+
print("Service discovery: GetCapabilities (fallback)")
345+
print("Available capabilities:", client.capabilities)
346+
else:
347+
print("Service discovery: Using default URLs")
341348
```
342349

343-
**Get detailed service information with capabilities:**
350+
**Why this approach?**
351+
352+
- **GetServices** provides the most accurate and detailed service information, but it's **optional** in the ONVIF specification
353+
- **GetCapabilities** is **mandatory** for all ONVIF-compliant devices, ensuring broader compatibility
354+
- **Default URLs** guarantee basic connectivity even with non-compliant devices
344355

345-
If you need detailed capability information for each service, call `GetServices` with `IncludeCapability=True`:
356+
**Get detailed service information:**
357+
358+
If you need comprehensive service details, you can manually call `GetServices` with capabilities:
346359

347360
```python
348361
device = client.devicemgmt()
349-
services = device.GetServices(IncludeCapability=True)
350-
351-
for service in services:
352-
print(f"Service: {service.Namespace}")
353-
print(f"Endpoint: {service.XAddr}")
354-
print(f"Version: {service.Version.Major}.{service.Version.Minor}")
355-
if hasattr(service, 'Capabilities') and service.Capabilities:
356-
print(f"Capabilities: {service.Capabilities}")
362+
363+
try:
364+
# Try to get detailed service information
365+
services = device.GetServices(IncludeCapability=True)
366+
for service in services:
367+
print(f"Service: {service.Namespace}")
368+
print(f"XAddr: {service.XAddr}")
369+
if hasattr(service, 'Capabilities'):
370+
print(f"Capabilities: {service.Capabilities}")
371+
except Exception:
372+
# Fallback to GetCapabilities for legacy devices
373+
capabilities = device.GetCapabilities()
374+
print("Capabilities:", capabilities)
357375
```
358376

359-
**Alternative: Use GetCapabilities for legacy compatibility:**
377+
**Access capabilities information:**
360378

361-
For backward compatibility or when you need a quick overview of major service categories, you can still use `GetCapabilities`:
379+
When using `GetCapabilities` fallback, you can access capability information:
362380

363381
```python
364-
capabilities = client.devicemgmt().GetCapabilities()
365-
print(capabilities)
366-
# Example output: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
382+
device = client.devicemgmt()
383+
384+
try:
385+
# Get capabilities information from device
386+
capabilities = device.GetCapabilities()
387+
388+
# Main services (always available)
389+
print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Not available'))
390+
print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Not available'))
391+
392+
# Extension services (device-dependent)
393+
ext = getattr(capabilities, 'Extension', None)
394+
if ext:
395+
print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Not available'))
396+
print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Not available'))
397+
print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Not available'))
398+
print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Not available'))
399+
except Exception as e:
400+
print(f"Error getting capabilities: {e}")
367401
```
368402

369403
> [!TIP]
370-
> The library handles service discovery automatically, so you typically don't need to call `GetServices` manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
404+
> The library handles service discovery automatically with intelligent fallback. You typically don't need to call discovery methods manually unless you need detailed capability information or want to refresh the service list after device configuration changes.
371405
372406
## Tested Devices
373407

README_ID.md

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ client = ONVIFClient(
312312
## Penemuan Layanan: Memahami Kapabilitas Perangkat
313313

314314
> [!WARNING]
315-
> Sebelum melakukan operasi apa pun pada perangkat ONVIF, sangat disarankan untuk menemukan layanan mana yang tersedia dan didukung oleh perangkat. Pustaka ini secara otomatis menggunakan `GetServices` selama inisialisasi untuk menemukan endpoint layanan, tetapi Anda juga dapat melakukan query layanan secara manual untuk informasi detail termasuk kapabilitas.
315+
> Sebelum melakukan operasi apa pun pada perangkat ONVIF, sangat disarankan untuk menemukan layanan mana yang tersedia dan didukung oleh perangkat. Pustaka ini secara otomatis melakukan penemuan layanan yang komprehensif selama inisialisasi menggunakan mekanisme fallback yang kuat.
316316
317317
**Mengapa menemukan layanan perangkat?**
318318

@@ -323,50 +323,84 @@ client = ONVIFClient(
323323

324324
**Cara kerja penemuan layanan di pustaka ini:**
325325

326-
`ONVIFClient` secara otomatis memanggil `GetServices` selama inisialisasi untuk membangun peta layanan. Peta ini digunakan secara internal untuk menyelesaikan endpoint layanan:
326+
`ONVIFClient` menggunakan **pendekatan penemuan 3-tingkat** untuk memaksimalkan kompatibilitas perangkat:
327+
328+
1. **GetServices (Preferensi)** - Mencoba `GetServices` terlebih dahulu untuk informasi layanan yang detail
329+
2. **GetCapabilities (Fallback)** - Menggunakan `GetCapabilities` jika `GetServices` tidak didukung
330+
3. **URL Default (Fallback Akhir)** - Menggunakan URL ONVIF standar sebagai pilihan terakhir
327331

328332
```python
329333
from onvif import ONVIFClient
330334

331335
client = ONVIFClient("192.168.1.17", 8000, "admin", "admin123")
332336

333-
# Akses layanan yang ditemukan
334-
print(client.services)
335-
# Contoh: [{'Namespace': 'http://www.onvif.org/ver10/device/wsdl', 'XAddr': '...', 'Version': {...}}, ...]
336-
337-
# Periksa peta layanan (namespace -> pemetaan XAddr)
338-
print(client._service_map)
339-
# Contoh: {'http://www.onvif.org/ver10/media/wsdl': 'http://192.168.1.17:8000/onvif/Media', ...}
337+
# Periksa metode penemuan mana yang digunakan
338+
if client.services:
339+
print("Penemuan layanan: GetServices (preferensi)")
340+
print("Layanan yang ditemukan:", len(client.services))
341+
print("Peta layanan:", client._service_map)
342+
elif client.capabilities:
343+
print("Penemuan layanan: GetCapabilities (fallback)")
344+
print("Kapabilitas yang tersedia:", client.capabilities)
345+
else:
346+
print("Penemuan layanan: Menggunakan URL default")
340347
```
341348

342-
**Dapatkan informasi layanan detail dengan kapabilitas:**
349+
**Mengapa pendekatan ini?**
350+
351+
- **GetServices** memberikan informasi layanan yang paling akurat dan detail, tetapi bersifat **opsional** dalam spesifikasi ONVIF
352+
- **GetCapabilities** bersifat **wajib** untuk semua perangkat yang sesuai dengan ONVIF, memastikan kompatibilitas yang lebih luas
353+
- **URL Default** menjamin konektivitas dasar bahkan dengan perangkat yang tidak sesuai
343354

344-
Jika Anda memerlukan informasi kapabilitas detail untuk setiap layanan, panggil `GetServices` dengan `IncludeCapability=True`:
355+
**Dapatkan informasi layanan detail:**
356+
357+
Jika Anda memerlukan detail layanan yang komprehensif, Anda dapat memanggil `GetServices` secara manual dengan kapabilitas:
345358

346359
```python
347360
device = client.devicemgmt()
348-
services = device.GetServices(IncludeCapability=True)
349-
350-
for service in services:
351-
print(f"Layanan: {service.Namespace}")
352-
print(f"Endpoint: {service.XAddr}")
353-
print(f"Versi: {service.Version.Major}.{service.Version.Minor}")
354-
if hasattr(service, 'Capabilities') and service.Capabilities:
355-
print(f"Kapabilitas: {service.Capabilities}")
361+
362+
try:
363+
# Coba dapatkan informasi layanan detail
364+
services = device.GetServices(IncludeCapability=True)
365+
for service in services:
366+
print(f"Layanan: {service.Namespace}")
367+
print(f"XAddr: {service.XAddr}")
368+
if hasattr(service, 'Capabilities'):
369+
print(f"Kapabilitas: {service.Capabilities}")
370+
except Exception:
371+
# Fallback ke GetCapabilities untuk perangkat legacy
372+
capabilities = device.GetCapabilities()
373+
print("Kapabilitas:", capabilities)
356374
```
357375

358-
**Alternatif: Gunakan GetCapabilities untuk kompatibilitas legacy:**
376+
**Akses informasi kapabilitas:**
359377

360-
Untuk kompatibilitas mundur atau ketika Anda memerlukan gambaran cepat tentang kategori layanan utama, Anda masih dapat menggunakan `GetCapabilities`:
378+
Saat menggunakan fallback `GetCapabilities`, Anda dapat mengakses informasi kapabilitas:
361379

362380
```python
363-
capabilities = client.devicemgmt().GetCapabilities()
364-
print(capabilities)
365-
# Contoh: {'Media': {'XAddr': '...', ...}, 'PTZ': {...}, 'Events': {...}, ...}
381+
device = client.devicemgmt()
382+
383+
try:
384+
# Dapatkan informasi kapabilitas dari perangkat
385+
capabilities = device.GetCapabilities()
386+
387+
# Layanan utama (selalu tersedia)
388+
print("Media XAddr:", getattr(capabilities.Media, 'XAddr', 'Tidak tersedia'))
389+
print("PTZ XAddr:", getattr(capabilities.PTZ, 'XAddr', 'Tidak tersedia'))
390+
391+
# Layanan Extension (tergantung perangkat)
392+
ext = getattr(capabilities, 'Extension', None)
393+
if ext:
394+
print("DeviceIO XAddr:", getattr(ext.DeviceIO, 'XAddr', 'Tidak tersedia'))
395+
print("Recording XAddr:", getattr(ext.Recording, 'XAddr', 'Tidak tersedia'))
396+
print("Search XAddr:", getattr(ext.Search, 'XAddr', 'Tidak tersedia'))
397+
print("Replay XAddr:", getattr(ext.Replay, 'XAddr', 'Tidak tersedia'))
398+
except Exception as e:
399+
print(f"Error mendapatkan kapabilitas: {e}")
366400
```
367401

368402
> [!TIP]
369-
> Pustaka menangani penemuan layanan secara otomatis, jadi Anda biasanya tidak perlu memanggil `GetServices` secara manual kecuali Anda memerlukan informasi kapabilitas detail atau ingin menyegarkan daftar layanan setelah perubahan konfigurasi perangkat.
403+
> Pustaka menangani penemuan layanan secara otomatis dengan fallback yang cerdas. Anda biasanya tidak perlu memanggil metode penemuan secara manual kecuali Anda memerlukan informasi kapabilitas detail atau ingin menyegarkan daftar layanan setelah perubahan konfigurasi perangkat.
370404
371405
## Perangkat yang Diuji
372406

0 commit comments

Comments
 (0)