10. Vendor-Specific Extensions¶
10.1 Logitech Extensions¶
Logitech-specific camera properties and control functions for PTZ, LED, and audio features. Available on supported Logitech camera models (C922, C930, C925, etc.).
LogitechProperty enum¶
10 vendor properties specific to Logitech cameras. Integer enum values mapped to hardware controls.
Properties:
Property |
Value |
Purpose |
|---|---|---|
|
0 |
LED indicator on/off/auto |
|
1 |
Automatic brightness adjustment |
|
2 |
Enable/disable face detection |
|
3 |
Low-light mode |
|
4 |
Auto-exposure control |
|
5 |
Auto white balance control |
|
6 |
Auto low-light adjustment |
|
7 |
PTZ pan movement speed |
|
8 |
PTZ tilt movement speed |
|
9 |
PTZ zoom movement speed |
Usage:
import duvc_ctl as duvc
device = duvc.devices()[^0]
result = duvc.get_logitech_property(device, duvc.LogitechProperty.LED_Mode)
if result.is_ok():
print(f"LED Mode: {result.value()}")
get_logitech_property() - read Logitech property¶
Get Logitech-specific property value. Returns Result type with error on unsupported models.
def get_logitech_property(
device: Device,
prop: LogitechProperty
) -> Okuint32 | Erruint32:
"""
Get Logitech property value.
Returns:
Okuint32 with value, or Erruint32 if unsupported/failed
"""
Usage:
import duvc_ctl as duvc
device = duvc.devices()[^0]
# Read LED mode
result = duvc.get_logitech_property(device, duvc.LogitechProperty.LED_Mode)
if result.is_ok():
print(f"LED: {result.value()}")
else:
print(f"Error: {result.error().description()}")
set_logitech_property() - write Logitech property¶
Set Logitech-specific property value. Range varies by property.
def set_logitech_property(
device: Device,
prop: LogitechProperty,
value: int
) -> Okvoid | Errvoid:
"""
Set Logitech property value.
Returns:
Okvoid on success, Errvoid on error
"""
Usage:
import duvc_ctl as duvc
device = duvc.devices()[^0]
# Set LED to auto (value = 2)
result = duvc.set_logitech_property(
device,
duvc.LogitechProperty.LED_Mode,
2
)
if result.is_ok():
print("LED set to auto")
else:
print(f"Failed: {result.error().description()}")
# Set pan speed (0-100)
duvc.set_logitech_property(device, duvc.LogitechProperty.Pan_Speed, 75)
supports_logitech_properties() - capability check¶
Query if device supports Logitech extensions. Some models lack vendor features.
def supports_logitech_properties(device: Device) -> bool:
"""Check if device supports Logitech extensions."""
Usage:
import duvc_ctl as duvc
device = duvc.devices()[^0]
if duvc.supports_logitech_properties(device):
print(f"{device.name} supports Logitech extensions")
# Safe to use Logitech-specific functions
result = duvc.get_logitech_property(
device,
duvc.LogitechProperty.LED_Mode
)
else:
print(f"{device.name} does not support Logitech extensions")
Capability detection:
import duvc_ctl as duvc
for device in duvc.devices():
has_logitech = duvc.supports_logitech_properties(device)
marker = "✓" if has_logitech else "✗"
print(f"{marker} {device.name}")
10.2 GUID & Vendor Properties¶
Low-level GUID handling for Windows COM interfaces and generic vendor property access. All GUID functions are Windows-only.
GUID wrapper type (Windows-only)¶
GUID object encapsulates Windows GUID structure. Immutable wrapper for interface identification and vendor-specific control.
class GUID:
"""Windows GUID wrapper (128-bit UUID)."""
def __str__(self) -> str: ...
def __bytes__(self) -> bytes: ...
guid_from_uuid() - UUID to GUID conversion (Windows-only)¶
Convert Python UUID to Windows GUID format.
def guid_from_uuid(uuid_obj: uuid.UUID) -> GUID:
"""Convert Python UUID to GUID."""
Usage:
import duvc_ctl as duvc
import uuid
standard_uuid = uuid.UUID("6994ad05-93f7-406d-611d-4222d440c8a0")
guid = duvc.guid_from_uuid(standard_uuid)
print(guid)
guid_from_pyobj() - flexible GUID creation (Windows-only)¶
Create GUID from string, bytes, or UUID object.
def guid_from_pyobj(obj: str | bytes | uuid.UUID) -> GUID:
"""Create GUID from string, bytes, or UUID."""
String formats:
{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}(with braces)XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX(no braces)XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX(hex only)
Usage:
import duvc_ctl as duvc
# From string
guid1 = duvc.guid_from_pyobj("{6994ad05-93f7-406d-611d-4222d440c8a0}")
# From bytes (16 bytes, little-endian GUID)
guid2 = duvc.guid_from_pyobj(b'\x05\xad\x94\x69\xf7\x93\x6d\x40...')
# From UUID
import uuid
guid3 = duvc.guid_from_pyobj(uuid.UUID("6994ad05-93f7-406d-611d-4222d440c8a0"))
VendorProperty type¶
Structured vendor property descriptor with metadata. Contains GUID, property ID, and access mode.
class VendorProperty:
"""Vendor-specific property descriptor."""
vendor_guid: GUID
property_id: int
access_mode: str # "read", "write", "read_write"
data_type: str # "int", "bytes", "string"
DeviceConnection type¶
Device connection context for vendor property operations. Wraps open camera handle.
class DeviceConnection:
"""Device connection for vendor operations."""
device: Device
is_open: bool
error: str | None
read_vendor_property() - get vendor property (Windows-only)¶
Read vendor-specific property using GUID and property ID.
def read_vendor_property(
device: Device,
vendor_guid: GUID,
property_id: int,
data_type: str = "int"
) -> Result[bytes | int]:
"""Read vendor property."""
Data types: "int" → int, "bytes" → bytes, "string" → str
Usage:
import duvc_ctl as duvc
device = duvc.devices()[0]
guid = duvc.guid_from_pyobj("{6994ad05-93f7-406d-611d-4222d440c8a0}")
result = duvc.read_vendor_property(device, guid, prop_id=0, data_type="int")
if result.is_ok():
value = result.value()
print(f"Property value: {value}")
else:
print(f"Error: {result.error().description()}")
write_vendor_property() - set vendor property (Windows-only)¶
Write vendor-specific property value.
def write_vendor_property(
device: Device,
vendor_guid: GUID,
property_id: int,
value: bytes | int | str,
data_type: str = "int"
) -> Result[None]:
"""Write vendor property."""
Usage:
import duvc_ctl as duvc
device = duvc.devices()[0]
guid = duvc.guid_from_pyobj("{6994ad05-93f7-406d-611d-4222d440c8a0}")
result = duvc.write_vendor_property(
device, guid, prop_id=0, value=100, data_type="int"
)
if result.is_ok():
print("Property set successfully")
else:
print(f"Error: {result.error().description()}")
get_vendor_property() - wrapper with error handling¶
High-level wrapper for read_vendor_property(). Raises exception on error.
def get_vendor_property(
device: Device,
vendor_guid: GUID,
property_id: int,
data_type: str = "int"
) -> bytes | int | str:
"""Read vendor property (exception on error)."""
Usage:
import duvc_ctl as duvc
device = duvc.devices()[0]
guid = duvc.guid_from_pyobj("{6994ad05-93f7-406d-611d-4222d440c8a0}")
try:
value = duvc.get_vendor_property(device, guid, 0, "int")
print(f"Property: {value}")
except duvc.DuvcError as e:
print(f"Error: {e}")
set_vendor_property() - wrapper with error handling¶
High-level wrapper for write_vendor_property(). Raises exception on error.
def set_vendor_property(
device: Device,
vendor_guid: GUID,
property_id: int,
value: bytes | int | str,
data_type: str = "int"
) -> None:
"""Write vendor property (exception on error)."""
Usage:
import duvc_ctl as duvc
device = duvc.devices()[0]
guid = duvc.guid_from_pyobj("{6994ad05-93f7-406d-611d-4222d440c8a0}")
try:
duvc.set_vendor_property(device, guid, 0, 100, "int")
print("Property set")
except duvc.DuvcError as e:
print(f"Error: {e}")
10.3 Abstract Interfaces for Extension¶
Plugin-style extensibility through abstract interface implementation. Python subclasses can override C++ interface methods using pybind11 trampolines.
IPlatformInterface abstract (Windows-only)¶
Abstract base for platform-specific operations. Defines hooks for device enumeration, property access, and system queries. Not used directly; subclass via Python or use built-in implementation.
class IPlatformInterface:
"""Platform implementation contract."""
def enumerate_devices(self) -> list[Device]: ...
def query_device_capabilities(self, device: Device) -> DeviceCapabilities: ...
def get_property(self, device: Device, prop_id: int) -> int: ...
def set_property(self, device: Device, prop_id: int, value: int) -> bool: ...
IDeviceConnection abstract (Windows-only)¶
Abstract connection context for vendor operations. Manages device lifecycle and low-level I/O. Override for custom transport layers or simulation.
class IDeviceConnection:
"""Device connection contract."""
def open(self, device: Device) -> bool: ...
def close(self) -> None: ...
def is_connected(self) -> bool: ...
def read_property(self, prop_id: int) -> bytes: ...
def write_property(self, prop_id: int, data: bytes) -> bool: ...
PyIPlatformInterface - Python subclassing trampoline¶
Pybind11 trampoline enabling Python classes to override interface methods. Automatic GIL management for C++→Python calls.
Usage:
import duvc_ctl as duvc
class CustomPlatform(duvc.IPlatformInterface):
def enumerate_devices(self):
print("Custom enumeration")
devices = []
# Custom device discovery logic
return devices
def query_device_capabilities(self, device):
# Custom capability query
return duvc.DeviceCapabilities()
def get_property(self, device, prop_id):
# Custom property read
return 0
def set_property(self, device, prop_id, value):
# Custom property write
return True
# Register with library
platform = CustomPlatform()
duvc.create_platform_interface(platform)
PyIDeviceConnection - Python subclassing trampoline¶
Trampoline for device connection override. Custom implementations handle non-standard transports or mock devices.
Usage:
import duvc_ctl as duvc
class MockConnection(duvc.IDeviceConnection):
def open(self, device):
print(f"Mock open: {device.name}")
return True
def close(self):
print("Mock close")
def is_connected(self):
return True
def read_property(self, prop_id):
# Return mock data
return b'\x00\x01\x02\x03'
def write_property(self, prop_id, data):
print(f"Mock write: {len(data)} bytes")
return True
conn = MockConnection()
# Pass to device operations
PYBIND11_OVERRIDE_PURE macro pattern¶
Macros used internally to forward Python method calls to C++. Not directly called by users, but understanding helps with debugging.
Invocation (internal):
// C++ side (pybind11 binding code)
class PyIPlatformInterface : public IPlatformInterface {
std::vector<Device> enumerate_devices() override {
PYBIND11_OVERRIDE_PURE(
std::vector<Device>,
IPlatformInterface,
enumerate_devices
);
}
};
When Python calls enumerate_devices(), pybind11 routes it through this macro, acquiring GIL and calling the Python implementation.
create_platform_interface() - factory registration¶
Register custom platform implementation. Library uses it for all subsequent operations.
def create_platform_interface(impl: IPlatformInterface) -> None:
"""Register custom platform implementation."""
Usage:
import duvc_ctl as duvc
class CustomPlatform(duvc.IPlatformInterface):
# ... implementation ...
pass
duvc.create_platform_interface(CustomPlatform())
# All library operations now use custom implementation
devices = duvc.devices() # Uses CustomPlatform.enumerate_devices()
Python custom implementation patterns¶
Pattern 1: Simulation/Testing
import duvc_ctl as duvc
class SimulatedPlatform(duvc.IPlatformInterface):
def __init__(self):
self.device_list = []
def enumerate_devices(self):
# Return mock devices
return self.device_list
def query_device_capabilities(self, device):
caps = duvc.DeviceCapabilities()
caps.supported_camera_properties = [duvc.CamProp.Pan, duvc.CamProp.Tilt]
return caps
def get_property(self, device, prop_id):
return 50 # Mock value
def set_property(self, device, prop_id, value):
return True # Always succeed
# Use simulated environment
duvc.create_platform_interface(SimulatedPlatform())
Pattern 2: Logging/Debugging wrapper
import duvc_ctl as duvc
class LoggingPlatform(duvc.IPlatformInterface):
def __init__(self, real_impl):
self.real = real_impl
def enumerate_devices(self):
print("TRACE: enumerate_devices called")
result = self.real.enumerate_devices()
print(f"TRACE: found {len(result)} devices")
return result
def get_property(self, device, prop_id):
print(f"TRACE: get_property device={device.name} prop={prop_id}")
value = self.real.get_property(device, prop_id)
print(f"TRACE: result={value}")
return value
def set_property(self, device, prop_id, value):
print(f"TRACE: set_property device={device.name} prop={prop_id} value={value}")
success = self.real.set_property(device, prop_id, value)
print(f"TRACE: success={success}")
return success
Pattern 3: Custom device transport
import duvc_ctl as duvc
class NetworkConnection(duvc.IDeviceConnection):
def __init__(self, host, port):
self.host = host
self.port = port
self.socket = None
def open(self, device):
# Connect to remote device
import socket
self.socket = socket.socket()
self.socket.connect((self.host, self.port))
return True
def is_connected(self):
return self.socket is not None
def read_property(self, prop_id):
# Send read request over network
self.socket.send(f"READ {prop_id}".encode())
return self.socket.recv(256)
def write_property(self, prop_id, data):
# Send write request over network
self.socket.send(f"WRITE {prop_id} {len(data)}".encode())
self.socket.send(data)
return True
def close(self):
if self.socket:
self.socket.close()
Important Notes:
Custom implementations must implement all abstract methods
GIL is held during Python method calls; avoid blocking
Exceptions in Python methods are converted to C++ exceptions
Return types must match interface contract exactly