bme680-homekit/app/config.py

117 lines
3.7 KiB
Python

import functools
import os
from pathlib import Path
from typing import Type, Tuple, Any, Dict
import yaml
from pydantic import BaseModel, Field
from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
config_file_default_location = "config.yaml"
class PrometheusSettings(BaseModel):
enabled: bool = Field(True, description="Enable prometheus metrics server.")
port: int = Field(8000, description="The prometheus metrics server port.")
class Bme680Settings(BaseModel):
enabled: bool = Field(True, description="If the sensor should be enabled on not.")
address: int = Field(
0x76, description="The I2C address of the sensor. Defaults to primary"
)
name: str = Field("Climate Sensor", description="The name of the sensor.")
class BridgeSettings(BaseModel):
display_name: str = Field(
"Bridge", description="The display name of the HAP bridge."
)
bme680: Bme680Settings = Field(
Bme680Settings(), description="Settings for the BME680 module."
)
class HomekitAccessoryProtocolSettings(BaseModel):
port: int = Field(
51826, description="The port of the homekit accessory protocol server."
)
persist_file: str = Field(
...,
description="The persistence file which holds the homekit accessory protocol server's state.",
)
bridge: BridgeSettings = Field(
BridgeSettings(), description="The HAP's default bridge settings."
)
class YamlConfigSettingsSource(PydanticBaseSettingsSource):
"""
A simple settings source class that loads variables from a YAML file
at the project's root.
Here we happen to choose to use the `env_file_encoding` from Config
when reading `config.yaml`
"""
@functools.lru_cache
def read_file_content(self):
encoding = self.config.get("env_file_encoding")
return yaml.safe_load(
Path(
os.getenv("HOMEKIT_CONFIG", default=config_file_default_location)
).read_text(encoding)
)
def get_field_value(
self, field: FieldInfo, field_name: str
) -> Tuple[Any, str, bool]:
file_content_json = self.read_file_content()
field_value = file_content_json.get(field_name)
return field_value, field_name, False
def prepare_field_value(
self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool
) -> Any:
return value
def __call__(self) -> Dict[str, Any]:
d: Dict[str, Any] = {}
for field_name, field in self.settings_cls.model_fields.items():
field_value, field_key, value_is_complex = self.get_field_value(
field, field_name
)
field_value = self.prepare_field_value(
field_name, field, field_value, value_is_complex
)
if field_value is not None:
d[field_key] = field_value
return d
class Settings(BaseSettings):
prometheus: PrometheusSettings = Field(
PrometheusSettings(), description="Settings for prometheus."
)
hap: HomekitAccessoryProtocolSettings = Field(
..., description="Homekit Accessory Protocol server settings."
)
@classmethod
def settings_customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
YamlConfigSettingsSource(settings_cls),
env_settings,
file_secret_settings,
)