Configuration API Reference¶
Configuration management handles device configuration storage and persistence.
BaseConfigManager¶
ucapi_framework.config.BaseConfigManager ¶
BaseConfigManager(data_path: str, add_handler: Callable[[DeviceT], None] | None = None, remove_handler: Callable[[DeviceT | None], None] | None = None, config_class: type[DeviceT] | None = None)
Bases: Generic[DeviceT]
Base class for device configuration management.
Handles: - Loading/storing configuration from/to JSON - CRUD operations (add, update, remove, get) - Configuration callbacks - Optional backup/restore support
Class Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
DeviceT
|
The device configuration dataclass type |
required |
Create a configuration instance.
:param data_path: Configuration path for the configuration file :param add_handler: Optional callback when device is added :param remove_handler: Optional callback when device is removed :param config_class: The configuration dataclass type (optional, auto-detected from type hints if not provided)
Attributes¶
Functions¶
contains ¶
Check if there's a device with the given device identifier.
:param device_id: Device identifier :return: True if device exists
add_or_update ¶
Add a new device or update if it already exists.
:param device: Device configuration to add or update
get ¶
Get device configuration for given identifier.
:param device_id: Device identifier :return: Device configuration or None
update ¶
Update a configured device and persist configuration.
:param device: Device configuration with updated values :return: True if device was updated, False if not found
remove ¶
Remove the given device configuration.
:param device_id: Device identifier :return: True if device was removed
store ¶
Store the configuration file.
:return: True if the configuration could be saved
load ¶
Load the configuration from file.
:return: True if the configuration could be loaded
get_device_id ¶
Extract device identifier from device configuration.
Default implementation: tries common attribute names (identifier, id, device_id). Override this if your device config uses a different attribute name.
:param device: Device configuration :return: Device identifier :raises AttributeError: If no valid ID attribute is found
get_backup_json ¶
Get configuration as JSON string for backup.
:return: JSON string representation of configuration
restore_from_backup_json ¶
Restore configuration from JSON string.
:param backup_json: JSON string containing configuration backup :return: True if restore was successful
migration_required ¶
Check if configuration migration is required.
Override this method to implement migration detection logic.
:return: True if migration is required
migrate
async
¶
Migrate configuration if required.
Override this method to implement migration logic.
:return: True if migration was successful
deserialize_device_auto ¶
Automatically deserialize device configuration with nested dataclass support.
This helper method automatically handles: - Nested dataclasses - Lists of dataclasses (e.g., list[LutronLightInfo]) - Primitive types
Use this in your deserialize_device() implementation:
Example
def deserialize_device(self, data: dict) -> MyDeviceConfig | None: return self.deserialize_device_auto(data, MyDeviceConfig)
For backward compatibility or custom logic, override specific fields:
Example
def deserialize_device(self, data: dict) -> MyDeviceConfig | None: # Let auto-deserialize handle nested dataclasses device = self.deserialize_device_auto(data, MyDeviceConfig) if device: # Add custom migration logic if not hasattr(device, 'new_field'): device.new_field = "default_value" return device
:param data: Dictionary with device data :param device_class: The device dataclass type :return: Device configuration or None if invalid
deserialize_device ¶
Deserialize device configuration from dictionary.
DEFAULT IMPLEMENTATION: Uses deserialize_device_auto() with the config class provided during initialization or inferred from the Generic type parameter.
Most integrations can use the default implementation without overriding:
class MyConfigManager(BaseConfigManager[MyDeviceConfig]):
pass # No override needed!
Or explicitly pass the config class:
manager = MyConfigManager(data_path, config_class=MyDeviceConfig)
Override only if you need custom logic:
def deserialize_device(self, data: dict) -> MyDeviceConfig | None:
# Auto-deserialize handles nested dataclasses
device = self.deserialize_device_auto(data, MyDeviceConfig)
if device:
# Custom migration logic
if 'old_field' in data:
device.new_field = migrate_value(data['old_field'])
# Custom post-processing
for light in device.lights:
light.name = light.name.replace("_", " ")
return device
:param data: Dictionary with device data :return: Device configuration or None if invalid
update_device_fields ¶
Update fields of existing device with values from updated device.
Default implementation updates all fields. Override for custom behavior.
:param existing: Existing device configuration (will be modified) :param updated: Updated device configuration (source of new values)