mirror of
				https://github.com/Akkudoktor-EOS/EOS.git
				synced 2025-11-03 00:06:20 +00:00 
			
		
		
		
	REST: Allow setting single config value
* /v1/config/{path} supports setting single config value (post body). Lists are
   supported as well by using the index:
    - general/latitude (value: 55.55)
    - optimize/ev_available_charge_rates_percent/0 (value: 42)
   Whole tree can be overriden as well (no merge):
    - optimize/ev_available_charge_rates_percent (value: [42, 43, 44]
 * ConfigEOS: Add set_config_value, get_config_value
			
			
This commit is contained in:
		
				
					committed by
					
						
						Bobby Noelte
					
				
			
			
				
	
			
			
			
						parent
						
							1bb74ed836
						
					
				
				
					commit
					94618f5f66
				
			@@ -257,6 +257,70 @@ Returns:
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## GET /v1/config/{path}
 | 
			
		||||
 | 
			
		||||
**Links**: [local](http://localhost:8503/docs#/default/fastapi_config_get_key_v1_config__path__get), [eos](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_config_get_key_v1_config__path__get)
 | 
			
		||||
 | 
			
		||||
Fastapi Config Get Key
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Get the value of a nested key or index in the config model.
 | 
			
		||||
 | 
			
		||||
Args:
 | 
			
		||||
    path (str): The nested path to the key (e.g., "general/latitude" or "optimize/nested_list/0").
 | 
			
		||||
 | 
			
		||||
Returns:
 | 
			
		||||
    value (Any): The value of the selected nested key.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Parameters**:
 | 
			
		||||
 | 
			
		||||
- `path` (path, required): The nested path to the configuration key (e.g., general/latitude).
 | 
			
		||||
 | 
			
		||||
**Responses**:
 | 
			
		||||
 | 
			
		||||
- **200**: Successful Response
 | 
			
		||||
 | 
			
		||||
- **422**: Validation Error
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## PUT /v1/config/{path}
 | 
			
		||||
 | 
			
		||||
**Links**: [local](http://localhost:8503/docs#/default/fastapi_config_put_key_v1_config__path__put), [eos](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_config_put_key_v1_config__path__put)
 | 
			
		||||
 | 
			
		||||
Fastapi Config Put Key
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Update a nested key or index in the config model.
 | 
			
		||||
 | 
			
		||||
Args:
 | 
			
		||||
    path (str): The nested path to the key (e.g., "general/latitude" or "optimize/nested_list/0").
 | 
			
		||||
    value (Any): The new value to assign to the key or index at path.
 | 
			
		||||
 | 
			
		||||
Returns:
 | 
			
		||||
    configuration (ConfigEOS): The current configuration after the update.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Parameters**:
 | 
			
		||||
 | 
			
		||||
- `path` (path, required): The nested path to the configuration key (e.g., general/latitude).
 | 
			
		||||
 | 
			
		||||
**Request Body**:
 | 
			
		||||
 | 
			
		||||
- `application/json`: {
 | 
			
		||||
  "description": "The value to assign to the specified configuration path.",
 | 
			
		||||
  "title": "Value"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
**Responses**:
 | 
			
		||||
 | 
			
		||||
- **200**: Successful Response
 | 
			
		||||
 | 
			
		||||
- **422**: Validation Error
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
## PUT /v1/measurement/data
 | 
			
		||||
 | 
			
		||||
**Links**: [local](http://localhost:8503/docs#/default/fastapi_measurement_data_put_v1_measurement_data_put), [eos](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_measurement_data_put_v1_measurement_data_put)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										577
									
								
								openapi.json
									
									
									
									
									
								
							
							
						
						
									
										577
									
								
								openapi.json
									
									
									
									
									
								
							@@ -106,246 +106,6 @@
 | 
			
		||||
                "title": "BaseBatteryParameters",
 | 
			
		||||
                "type": "object"
 | 
			
		||||
            },
 | 
			
		||||
            "GeneralSettings-Input": {
 | 
			
		||||
                "description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n    latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n    longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n    timezone (Optional[str]): Computed time zone string based on the specified latitude\n        and longitude.\n\nValidators:\n    validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n    validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
 | 
			
		||||
                "properties": {
 | 
			
		||||
                    "data_cache_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "cache",
 | 
			
		||||
                        "description": "Sub-path for the EOS cache data directory.",
 | 
			
		||||
                        "title": "Data Cache Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_folder_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS data directory.",
 | 
			
		||||
                        "examples": [
 | 
			
		||||
                            null,
 | 
			
		||||
                            "/home/eos/data"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "title": "Data Folder Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_output_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "output",
 | 
			
		||||
                        "description": "Sub-path for the EOS output data directory.",
 | 
			
		||||
                        "title": "Data Output Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "latitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 90.0,
 | 
			
		||||
                                "minimum": -90.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 52.52,
 | 
			
		||||
                        "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
 | 
			
		||||
                        "title": "Latitude"
 | 
			
		||||
                    },
 | 
			
		||||
                    "longitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 180.0,
 | 
			
		||||
                                "minimum": -180.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 13.405,
 | 
			
		||||
                        "description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
 | 
			
		||||
                        "title": "Longitude"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                "title": "GeneralSettings",
 | 
			
		||||
                "type": "object"
 | 
			
		||||
            },
 | 
			
		||||
            "GeneralSettings-Output": {
 | 
			
		||||
                "description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n    latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n    longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n    timezone (Optional[str]): Computed time zone string based on the specified latitude\n        and longitude.\n\nValidators:\n    validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n    validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
 | 
			
		||||
                "properties": {
 | 
			
		||||
                    "config_file_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS configuration file.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Config File Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "config_folder_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS configuration directory.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Config Folder Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_cache_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Compute data_cache_path based on data_folder_path.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Data Cache Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_cache_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "cache",
 | 
			
		||||
                        "description": "Sub-path for the EOS cache data directory.",
 | 
			
		||||
                        "title": "Data Cache Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_folder_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS data directory.",
 | 
			
		||||
                        "examples": [
 | 
			
		||||
                            null,
 | 
			
		||||
                            "/home/eos/data"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "title": "Data Folder Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_output_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Compute data_output_path based on data_folder_path.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Data Output Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_output_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "output",
 | 
			
		||||
                        "description": "Sub-path for the EOS output data directory.",
 | 
			
		||||
                        "title": "Data Output Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "latitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 90.0,
 | 
			
		||||
                                "minimum": -90.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 52.52,
 | 
			
		||||
                        "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
 | 
			
		||||
                        "title": "Latitude"
 | 
			
		||||
                    },
 | 
			
		||||
                    "longitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 180.0,
 | 
			
		||||
                                "minimum": -180.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 13.405,
 | 
			
		||||
                        "description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
 | 
			
		||||
                        "title": "Longitude"
 | 
			
		||||
                    },
 | 
			
		||||
                    "timezone": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Compute timezone based on latitude and longitude.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Timezone"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                "required": [
 | 
			
		||||
                    "timezone",
 | 
			
		||||
                    "data_output_path",
 | 
			
		||||
                    "data_cache_path",
 | 
			
		||||
                    "config_folder_path",
 | 
			
		||||
                    "config_file_path"
 | 
			
		||||
                ],
 | 
			
		||||
                "title": "GeneralSettings",
 | 
			
		||||
                "type": "object"
 | 
			
		||||
            },
 | 
			
		||||
            "ConfigEOS": {
 | 
			
		||||
                "additionalProperties": false,
 | 
			
		||||
                "description": "Singleton configuration handler for the EOS application.\n\nConfigEOS extends `SettingsEOS` with support for  default configuration paths and automatic\ninitialization.\n\n`ConfigEOS` ensures that only one instance of the class is created throughout the application,\nallowing consistent access to EOS configuration settings. This singleton instance loads\nconfiguration data from a predefined set of directories or creates a default configuration if\nnone is found.\n\nInitialization Process:\n  - Upon instantiation, the singleton instance attempts to load a configuration file in this order:\n    1. The directory specified by the `EOS_CONFIG_DIR` environment variable\n    2. The directory specified by the `EOS_DIR` environment variable.\n    3. A platform specific default directory for EOS.\n    4. The current working directory.\n  - The first available configuration file found in these directories is loaded.\n  - If no configuration file is found, a default configuration file is created in the platform\n    specific default directory, and default settings are loaded into it.\n\nAttributes from the loaded configuration are accessible directly as instance attributes of\n`ConfigEOS`, providing a centralized, shared configuration object for EOS.\n\nSingleton Behavior:\n  - This class uses the `SingletonMixin` to ensure that all requests for `ConfigEOS` return\n    the same instance, which contains the most up-to-date configuration. Modifying the configuration\n    in one part of the application reflects across all references to this class.\n\nAttributes:\n    config_folder_path (Optional[Path]): Path to the configuration directory.\n    config_file_path (Optional[Path]): Path to the configuration file.\n\nRaises:\n    FileNotFoundError: If no configuration file is found, and creating a default configuration fails.\n\nExample:\n    To initialize and access configuration attributes (only one instance is created):\n    ```python\n    config_eos = ConfigEOS()  # Always returns the same instance\n    print(config_eos.prediction.hours)  # Access a setting from the loaded configuration\n    ```",
 | 
			
		||||
@@ -877,6 +637,246 @@
 | 
			
		||||
                "title": "ForecastResponse",
 | 
			
		||||
                "type": "object"
 | 
			
		||||
            },
 | 
			
		||||
            "GeneralSettings-Input": {
 | 
			
		||||
                "description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n    latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n    longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n    timezone (Optional[str]): Computed time zone string based on the specified latitude\n        and longitude.\n\nValidators:\n    validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n    validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
 | 
			
		||||
                "properties": {
 | 
			
		||||
                    "data_cache_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "cache",
 | 
			
		||||
                        "description": "Sub-path for the EOS cache data directory.",
 | 
			
		||||
                        "title": "Data Cache Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_folder_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS data directory.",
 | 
			
		||||
                        "examples": [
 | 
			
		||||
                            null,
 | 
			
		||||
                            "/home/eos/data"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "title": "Data Folder Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_output_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "output",
 | 
			
		||||
                        "description": "Sub-path for the EOS output data directory.",
 | 
			
		||||
                        "title": "Data Output Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "latitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 90.0,
 | 
			
		||||
                                "minimum": -90.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 52.52,
 | 
			
		||||
                        "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
 | 
			
		||||
                        "title": "Latitude"
 | 
			
		||||
                    },
 | 
			
		||||
                    "longitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 180.0,
 | 
			
		||||
                                "minimum": -180.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 13.405,
 | 
			
		||||
                        "description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
 | 
			
		||||
                        "title": "Longitude"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                "title": "GeneralSettings",
 | 
			
		||||
                "type": "object"
 | 
			
		||||
            },
 | 
			
		||||
            "GeneralSettings-Output": {
 | 
			
		||||
                "description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n    latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n    longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n    timezone (Optional[str]): Computed time zone string based on the specified latitude\n        and longitude.\n\nValidators:\n    validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n    validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
 | 
			
		||||
                "properties": {
 | 
			
		||||
                    "config_file_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS configuration file.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Config File Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "config_folder_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS configuration directory.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Config Folder Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_cache_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Compute data_cache_path based on data_folder_path.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Data Cache Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_cache_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "cache",
 | 
			
		||||
                        "description": "Sub-path for the EOS cache data directory.",
 | 
			
		||||
                        "title": "Data Cache Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_folder_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Path to EOS data directory.",
 | 
			
		||||
                        "examples": [
 | 
			
		||||
                            null,
 | 
			
		||||
                            "/home/eos/data"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "title": "Data Folder Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_output_path": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Compute data_output_path based on data_folder_path.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Data Output Path"
 | 
			
		||||
                    },
 | 
			
		||||
                    "data_output_subpath": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "format": "path",
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": "output",
 | 
			
		||||
                        "description": "Sub-path for the EOS output data directory.",
 | 
			
		||||
                        "title": "Data Output Subpath"
 | 
			
		||||
                    },
 | 
			
		||||
                    "latitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 90.0,
 | 
			
		||||
                                "minimum": -90.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 52.52,
 | 
			
		||||
                        "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
 | 
			
		||||
                        "title": "Latitude"
 | 
			
		||||
                    },
 | 
			
		||||
                    "longitude": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "maximum": 180.0,
 | 
			
		||||
                                "minimum": -180.0,
 | 
			
		||||
                                "type": "number"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "default": 13.405,
 | 
			
		||||
                        "description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
 | 
			
		||||
                        "title": "Longitude"
 | 
			
		||||
                    },
 | 
			
		||||
                    "timezone": {
 | 
			
		||||
                        "anyOf": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "type": "null"
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "description": "Compute timezone based on latitude and longitude.",
 | 
			
		||||
                        "readOnly": true,
 | 
			
		||||
                        "title": "Timezone"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                "required": [
 | 
			
		||||
                    "timezone",
 | 
			
		||||
                    "data_output_path",
 | 
			
		||||
                    "data_cache_path",
 | 
			
		||||
                    "config_folder_path",
 | 
			
		||||
                    "config_file_path"
 | 
			
		||||
                ],
 | 
			
		||||
                "title": "GeneralSettings",
 | 
			
		||||
                "type": "object"
 | 
			
		||||
            },
 | 
			
		||||
            "GesamtlastRequest": {
 | 
			
		||||
                "properties": {
 | 
			
		||||
                    "hours": {
 | 
			
		||||
@@ -3166,6 +3166,103 @@
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "/v1/config/{path}": {
 | 
			
		||||
            "get": {
 | 
			
		||||
                "description": "Get the value of a nested key or index in the config model.\n\nArgs:\n    path (str): The nested path to the key (e.g., \"general/latitude\" or \"optimize/nested_list/0\").\n\nReturns:\n    value (Any): The value of the selected nested key.",
 | 
			
		||||
                "operationId": "fastapi_config_get_key_v1_config__path__get",
 | 
			
		||||
                "parameters": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "description": "The nested path to the configuration key (e.g., general/latitude).",
 | 
			
		||||
                        "in": "path",
 | 
			
		||||
                        "name": "path",
 | 
			
		||||
                        "required": true,
 | 
			
		||||
                        "schema": {
 | 
			
		||||
                            "description": "The nested path to the configuration key (e.g., general/latitude).",
 | 
			
		||||
                            "title": "Path",
 | 
			
		||||
                            "type": "string"
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "responses": {
 | 
			
		||||
                    "200": {
 | 
			
		||||
                        "content": {
 | 
			
		||||
                            "application/json": {
 | 
			
		||||
                                "schema": {}
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        "description": "Successful Response"
 | 
			
		||||
                    },
 | 
			
		||||
                    "422": {
 | 
			
		||||
                        "content": {
 | 
			
		||||
                            "application/json": {
 | 
			
		||||
                                "schema": {
 | 
			
		||||
                                    "$ref": "#/components/schemas/HTTPValidationError"
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        "description": "Validation Error"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                "summary": "Fastapi Config Get Key",
 | 
			
		||||
                "tags": [
 | 
			
		||||
                    "config"
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            "put": {
 | 
			
		||||
                "description": "Update a nested key or index in the config model.\n\nArgs:\n    path (str): The nested path to the key (e.g., \"general/latitude\" or \"optimize/nested_list/0\").\n    value (Any): The new value to assign to the key or index at path.\n\nReturns:\n    configuration (ConfigEOS): The current configuration after the update.",
 | 
			
		||||
                "operationId": "fastapi_config_put_key_v1_config__path__put",
 | 
			
		||||
                "parameters": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "description": "The nested path to the configuration key (e.g., general/latitude).",
 | 
			
		||||
                        "in": "path",
 | 
			
		||||
                        "name": "path",
 | 
			
		||||
                        "required": true,
 | 
			
		||||
                        "schema": {
 | 
			
		||||
                            "description": "The nested path to the configuration key (e.g., general/latitude).",
 | 
			
		||||
                            "title": "Path",
 | 
			
		||||
                            "type": "string"
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "requestBody": {
 | 
			
		||||
                    "content": {
 | 
			
		||||
                        "application/json": {
 | 
			
		||||
                            "schema": {
 | 
			
		||||
                                "description": "The value to assign to the specified configuration path.",
 | 
			
		||||
                                "title": "Value"
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    "required": true
 | 
			
		||||
                },
 | 
			
		||||
                "responses": {
 | 
			
		||||
                    "200": {
 | 
			
		||||
                        "content": {
 | 
			
		||||
                            "application/json": {
 | 
			
		||||
                                "schema": {
 | 
			
		||||
                                    "$ref": "#/components/schemas/ConfigEOS"
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        "description": "Successful Response"
 | 
			
		||||
                    },
 | 
			
		||||
                    "422": {
 | 
			
		||||
                        "content": {
 | 
			
		||||
                            "application/json": {
 | 
			
		||||
                                "schema": {
 | 
			
		||||
                                    "$ref": "#/components/schemas/HTTPValidationError"
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        "description": "Validation Error"
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                "summary": "Fastapi Config Put Key",
 | 
			
		||||
                "tags": [
 | 
			
		||||
                    "config"
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "/v1/measurement/data": {
 | 
			
		||||
            "put": {
 | 
			
		||||
                "description": "Merge the measurement data given as datetime data into EOS measurements.",
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ from akkudoktoreos.core.coreabc import SingletonMixin
 | 
			
		||||
from akkudoktoreos.core.decorators import classproperty
 | 
			
		||||
from akkudoktoreos.core.logging import get_logger
 | 
			
		||||
from akkudoktoreos.core.logsettings import LoggingCommonSettings
 | 
			
		||||
from akkudoktoreos.core.pydantic import merge_models
 | 
			
		||||
from akkudoktoreos.core.pydantic import access_nested_value, merge_models
 | 
			
		||||
from akkudoktoreos.devices.settings import DevicesCommonSettings
 | 
			
		||||
from akkudoktoreos.measurement.measurement import MeasurementCommonSettings
 | 
			
		||||
from akkudoktoreos.optimization.optimization import OptimizationCommonSettings
 | 
			
		||||
@@ -379,6 +379,32 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
 | 
			
		||||
        """
 | 
			
		||||
        self._setup()
 | 
			
		||||
 | 
			
		||||
    def set_config_value(self, path: str, value: Any) -> None:
 | 
			
		||||
        """Set a configuration value based on the provided path.
 | 
			
		||||
 | 
			
		||||
        Supports string paths (with '/' separators) or sequence paths (list/tuple).
 | 
			
		||||
        Trims leading and trailing '/' from string paths.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            path (str): The path to the configuration key (e.g., "key1/key2/key3" or key1/key2/0).
 | 
			
		||||
            value (Any]): The value to set.
 | 
			
		||||
        """
 | 
			
		||||
        access_nested_value(self, path, True, value)
 | 
			
		||||
 | 
			
		||||
    def get_config_value(self, path: str) -> Any:
 | 
			
		||||
        """Get a configuration value based on the provided path.
 | 
			
		||||
 | 
			
		||||
        Supports string paths (with '/' separators) or sequence paths (list/tuple).
 | 
			
		||||
        Trims leading and trailing '/' from string paths.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            path (str): The path to the configuration key (e.g., "key1/key2/key3" or key1/key2/0).
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Any: The retrieved value.
 | 
			
		||||
        """
 | 
			
		||||
        return access_nested_value(self, path, False)
 | 
			
		||||
 | 
			
		||||
    def _create_initial_config_file(self) -> None:
 | 
			
		||||
        if self.general.config_file_path and not self.general.config_file_path.exists():
 | 
			
		||||
            self.general.config_file_path.parent.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,70 @@ def merge_models(source: BaseModel, update_dict: dict[str, Any]) -> dict[str, An
 | 
			
		||||
    return merged_dict
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def access_nested_value(
 | 
			
		||||
    model: BaseModel, path: str, setter: bool, value: Optional[Any] = None
 | 
			
		||||
) -> Any:
 | 
			
		||||
    """Get or set a nested model value based on the provided path.
 | 
			
		||||
 | 
			
		||||
    Supports string paths (with '/' separators) or sequence paths (list/tuple).
 | 
			
		||||
    Trims leading and trailing '/' from string paths.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        model (BaseModel): The model object for partial assignment.
 | 
			
		||||
        path (str): The path to the model key (e.g., "key1/key2/key3" or key1/key2/0).
 | 
			
		||||
        setter (bool): True to set value at path, False to return value at path.
 | 
			
		||||
        value (Optional[Any]): The value to set.
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        Any: The retrieved value if acting as a getter, or None if setting a value.
 | 
			
		||||
    """
 | 
			
		||||
    path_elements = path.strip("/").split("/")
 | 
			
		||||
 | 
			
		||||
    cfg: Any = model
 | 
			
		||||
    parent: BaseModel = model
 | 
			
		||||
    model_key: str = ""
 | 
			
		||||
 | 
			
		||||
    for i, key in enumerate(path_elements):
 | 
			
		||||
        is_final_key = i == len(path_elements) - 1
 | 
			
		||||
 | 
			
		||||
        if isinstance(cfg, list):
 | 
			
		||||
            try:
 | 
			
		||||
                idx = int(key)
 | 
			
		||||
                if is_final_key:
 | 
			
		||||
                    if not setter:  # Getter
 | 
			
		||||
                        return cfg[idx]
 | 
			
		||||
                    else:  # Setter
 | 
			
		||||
                        new_list = list(cfg)
 | 
			
		||||
                        new_list[idx] = value
 | 
			
		||||
                        # Trigger validation
 | 
			
		||||
                        setattr(parent, model_key, new_list)
 | 
			
		||||
                else:
 | 
			
		||||
                    cfg = cfg[idx]
 | 
			
		||||
            except ValidationError as e:
 | 
			
		||||
                raise ValueError(f"Error updating model: {e}") from e
 | 
			
		||||
            except (ValueError, IndexError) as e:
 | 
			
		||||
                raise IndexError(f"Invalid list index at {path}: {key}") from e
 | 
			
		||||
 | 
			
		||||
        elif isinstance(cfg, BaseModel):
 | 
			
		||||
            parent = cfg
 | 
			
		||||
            model_key = key
 | 
			
		||||
            if is_final_key:
 | 
			
		||||
                if not setter:  # Getter
 | 
			
		||||
                    return getattr(cfg, key)
 | 
			
		||||
                else:  # Setter
 | 
			
		||||
                    try:
 | 
			
		||||
                        # Verification also if nested value is provided opposed to just setattr
 | 
			
		||||
                        # Will merge partial assignment
 | 
			
		||||
                        cfg = cfg.__pydantic_validator__.validate_assignment(cfg, key, value)
 | 
			
		||||
                    except Exception as e:
 | 
			
		||||
                        raise ValueError(f"Error updating model: {e}") from e
 | 
			
		||||
            else:
 | 
			
		||||
                cfg = getattr(cfg, key)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            raise KeyError(f"Key '{key}' not found in model.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PydanticTypeAdapterDateTime(TypeAdapter[pendulum.DateTime]):
 | 
			
		||||
    """Custom type adapter for Pendulum DateTime fields."""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,9 @@ from typing import Annotated, Any, AsyncGenerator, Dict, List, Optional, Union
 | 
			
		||||
 | 
			
		||||
import httpx
 | 
			
		||||
import uvicorn
 | 
			
		||||
from fastapi import FastAPI, Query, Request
 | 
			
		||||
from fastapi import Body, FastAPI
 | 
			
		||||
from fastapi import Path as FastapiPath
 | 
			
		||||
from fastapi import Query, Request
 | 
			
		||||
from fastapi.exceptions import HTTPException
 | 
			
		||||
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse, Response
 | 
			
		||||
 | 
			
		||||
@@ -302,6 +304,58 @@ def fastapi_config_put(settings: SettingsEOS) -> ConfigEOS:
 | 
			
		||||
    return config_eos
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.put("/v1/config/{path:path}", tags=["config"])
 | 
			
		||||
def fastapi_config_put_key(
 | 
			
		||||
    path: str = FastapiPath(
 | 
			
		||||
        ..., description="The nested path to the configuration key (e.g., general/latitude)."
 | 
			
		||||
    ),
 | 
			
		||||
    value: Any = Body(..., description="The value to assign to the specified configuration path."),
 | 
			
		||||
) -> ConfigEOS:
 | 
			
		||||
    """Update a nested key or index in the config model.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        path (str): The nested path to the key (e.g., "general/latitude" or "optimize/nested_list/0").
 | 
			
		||||
        value (Any): The new value to assign to the key or index at path.
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        configuration (ConfigEOS): The current configuration after the update.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        config_eos.set_config_value(path, value)
 | 
			
		||||
    except IndexError as e:
 | 
			
		||||
        raise HTTPException(status_code=400, detail=str(e))
 | 
			
		||||
    except KeyError as e:
 | 
			
		||||
        raise HTTPException(status_code=404, detail=str(e))
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        raise HTTPException(status_code=400, detail=str(e))
 | 
			
		||||
 | 
			
		||||
    return config_eos
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.get("/v1/config/{path:path}", tags=["config"])
 | 
			
		||||
def fastapi_config_get_key(
 | 
			
		||||
    path: str = FastapiPath(
 | 
			
		||||
        ..., description="The nested path to the configuration key (e.g., general/latitude)."
 | 
			
		||||
    ),
 | 
			
		||||
) -> Response:
 | 
			
		||||
    """Get the value of a nested key or index in the config model.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        path (str): The nested path to the key (e.g., "general/latitude" or "optimize/nested_list/0").
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        value (Any): The value of the selected nested key.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return config_eos.get_config_value(path)
 | 
			
		||||
    except IndexError as e:
 | 
			
		||||
        raise HTTPException(status_code=400, detail=str(e))
 | 
			
		||||
    except KeyError as e:
 | 
			
		||||
        raise HTTPException(status_code=404, detail=str(e))
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        raise HTTPException(status_code=400, detail=str(e))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.get("/v1/measurement/keys", tags=["measurement"])
 | 
			
		||||
def fastapi_measurement_keys_get() -> list[str]:
 | 
			
		||||
    """Get a list of available measurement keys."""
 | 
			
		||||
 
 | 
			
		||||
@@ -212,3 +212,165 @@ def test_config_common_settings_timezone_none_when_coordinates_missing():
 | 
			
		||||
    assert config_no_latitude.timezone is None
 | 
			
		||||
    assert config_no_longitude.timezone is None
 | 
			
		||||
    assert config_no_coords.timezone is None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Test partial assignments and possible side effects
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    "path, value, expected, exception",
 | 
			
		||||
    [
 | 
			
		||||
        # Correct value assignment
 | 
			
		||||
        (
 | 
			
		||||
            "general/latitude",
 | 
			
		||||
            42.0,
 | 
			
		||||
            [("general.latitude", 42.0), ("general.longitude", 13.405)],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        # Correct value assignment (trailing /)
 | 
			
		||||
        (
 | 
			
		||||
            "general/latitude/",
 | 
			
		||||
            41,
 | 
			
		||||
            [("general.latitude", 41.0), ("general.longitude", 13.405)],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        # Correct value assignment (cast)
 | 
			
		||||
        (
 | 
			
		||||
            "general/latitude",
 | 
			
		||||
            "43.0",
 | 
			
		||||
            [("general.latitude", 43.0), ("general.longitude", 13.405)],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid value assignment (constraint)
 | 
			
		||||
        (
 | 
			
		||||
            "general/latitude",
 | 
			
		||||
            91.0,
 | 
			
		||||
            [("general.latitude", 52.52), ("general.longitude", 13.405)],
 | 
			
		||||
            ValueError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid value assignment (type)
 | 
			
		||||
        (
 | 
			
		||||
            "general/latitude",
 | 
			
		||||
            "test",
 | 
			
		||||
            [("general.latitude", 52.52), ("general.longitude", 13.405)],
 | 
			
		||||
            ValueError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid path
 | 
			
		||||
        (
 | 
			
		||||
            "general/latitude/test",
 | 
			
		||||
            "",
 | 
			
		||||
            [("general.latitude", 52.52), ("general.longitude", 13.405)],
 | 
			
		||||
            KeyError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Correct value nested assignment
 | 
			
		||||
        (
 | 
			
		||||
            "general",
 | 
			
		||||
            {"latitude": 22},
 | 
			
		||||
            [("general.latitude", 22.0), ("general.longitude", 13.405)],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid value nested assignment
 | 
			
		||||
        (
 | 
			
		||||
            "general",
 | 
			
		||||
            {"latitude": "test"},
 | 
			
		||||
            [("general.latitude", 52.52), ("general.longitude", 13.405)],
 | 
			
		||||
            ValueError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Correct value for list
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent/0",
 | 
			
		||||
            0.1,
 | 
			
		||||
            [
 | 
			
		||||
                (
 | 
			
		||||
                    "optimization.ev_available_charge_rates_percent",
 | 
			
		||||
                    [0.1, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
 | 
			
		||||
                )
 | 
			
		||||
            ],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid value for list
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent/0",
 | 
			
		||||
            "invalid",
 | 
			
		||||
            [
 | 
			
		||||
                (
 | 
			
		||||
                    "optimization.ev_available_charge_rates_percent",
 | 
			
		||||
                    [0.0, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
 | 
			
		||||
                )
 | 
			
		||||
            ],
 | 
			
		||||
            ValueError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid index (out of bound)
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent/10",
 | 
			
		||||
            0,
 | 
			
		||||
            [
 | 
			
		||||
                (
 | 
			
		||||
                    "optimization.ev_available_charge_rates_percent",
 | 
			
		||||
                    [0.0, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
 | 
			
		||||
                )
 | 
			
		||||
            ],
 | 
			
		||||
            IndexError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Invalid index (no number)
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent/test",
 | 
			
		||||
            0,
 | 
			
		||||
            [
 | 
			
		||||
                (
 | 
			
		||||
                    "optimization.ev_available_charge_rates_percent",
 | 
			
		||||
                    [0.0, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
 | 
			
		||||
                )
 | 
			
		||||
            ],
 | 
			
		||||
            IndexError,
 | 
			
		||||
        ),
 | 
			
		||||
        # Unset value (set None)
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent",
 | 
			
		||||
            None,
 | 
			
		||||
            [
 | 
			
		||||
                (
 | 
			
		||||
                    "optimization.ev_available_charge_rates_percent",
 | 
			
		||||
                    None,
 | 
			
		||||
                )
 | 
			
		||||
            ],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
def test_set_nested_key(path, value, expected, exception, config_eos):
 | 
			
		||||
    if not exception:
 | 
			
		||||
        config_eos.set_config_value(path, value)
 | 
			
		||||
        for expected_path, expected_value in expected:
 | 
			
		||||
            assert eval(f"config_eos.{expected_path}") == expected_value
 | 
			
		||||
    else:
 | 
			
		||||
        with pytest.raises(exception):
 | 
			
		||||
            config_eos.set_config_value(path, value)
 | 
			
		||||
        for expected_path, expected_value in expected:
 | 
			
		||||
            assert eval(f"config_eos.{expected_path}") == expected_value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize(
 | 
			
		||||
    "path, expected_value, exception",
 | 
			
		||||
    [
 | 
			
		||||
        ("general/latitude", 52.52, None),
 | 
			
		||||
        ("general/latitude/", 52.52, None),
 | 
			
		||||
        ("general/latitude/test", None, KeyError),
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent/1",
 | 
			
		||||
            0.375,
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
        ("optimization/ev_available_charge_rates_percent/10", 0, IndexError),
 | 
			
		||||
        ("optimization/ev_available_charge_rates_percent/test", 0, IndexError),
 | 
			
		||||
        (
 | 
			
		||||
            "optimization/ev_available_charge_rates_percent",
 | 
			
		||||
            [0.0, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
 | 
			
		||||
            None,
 | 
			
		||||
        ),
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
def test_get_nested_key(path, expected_value, exception, config_eos):
 | 
			
		||||
    if not exception:
 | 
			
		||||
        assert config_eos.get_config_value(path) == expected_value
 | 
			
		||||
    else:
 | 
			
		||||
        with pytest.raises(exception):
 | 
			
		||||
            config_eos.get_config_value(path)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user