Source code for nwp500.field_factory

"""Field factory for creating typed Pydantic fields with metadata templates.

This module provides convenience functions for creating Pydantic fields with
standard metadata (device_class, unit_of_measurement, etc.) pre-configured,
reducing boilerplate in models while maintaining type safety.

Each factory function creates a Pydantic Field with metadata for Home Assistant
integration:

- temperature_field: Adds unit_of_measurement, device_class='temperature',
  suggested_display_precision
- signal_strength_field: Adds unit_of_measurement,
  device_class='signal_strength'
- energy_field: Adds unit_of_measurement, device_class='energy'
- power_field: Adds unit_of_measurement, device_class='power'

Example:
    >>> from nwp500.field_factory import temperature_field
    >>> class MyModel(BaseModel):
    ...     temp: float = temperature_field("DHW Temperature", unit="°F")
"""

from typing import Any, cast

from pydantic import Field

__all__ = [
    "temperature_field",
    "signal_strength_field",
    "energy_field",
    "power_field",
]


def _metadata_field(
    description: str,
    metadata: dict[str, Any],
    default: Any,
    kwargs: dict[str, Any],
) -> Any:
    """Build a Pydantic Field with device metadata in json_schema_extra.

    Args:
        description: Field description
        metadata: Base json_schema_extra metadata for this field type
        default: Default value or Pydantic default
        kwargs: Additional Pydantic Field arguments; a caller-provided
            json_schema_extra dict is merged over the base metadata

    Returns:
        Pydantic Field with the merged metadata
    """
    json_schema_extra = dict(metadata)
    if "json_schema_extra" in kwargs:
        extra = kwargs.pop("json_schema_extra")
        if isinstance(extra, dict):
            # Explicitly cast to dict[str, Any] for type safety
            json_schema_extra.update(cast(dict[str, Any], extra))

    return Field(
        default=default,
        description=description,
        json_schema_extra=json_schema_extra,
        **kwargs,
    )


[docs] def temperature_field( description: str, *, unit: str = "°F", default: Any = None, **kwargs: Any, ) -> Any: """Create a temperature field with standard Home Assistant metadata. The unit parameter is critical for tools consuming this library (e.g., Home Assistant) to correctly interpret the values. While the actual displayed unit is dynamic based on device temperature_type setting (Celsius or Fahrenheit), the unit parameter in json_schema_extra provides the default/fallback unit and schema documentation for proper integration. Args: description: Field description unit: Temperature unit (default: °F). Used in json_schema_extra for Home Assistant and other integrations to understand value units. Displayed units are dynamic based on device temperature_type. default: Default value or Pydantic default **kwargs: Additional Pydantic Field arguments Returns: Pydantic Field with temperature metadata """ return _metadata_field( description, { "unit_of_measurement": unit, "device_class": "temperature", "suggested_display_precision": 1, }, default, kwargs, )
[docs] def signal_strength_field( description: str, *, unit: str = "dBm", default: Any = None, **kwargs: Any, ) -> Any: """Create a signal strength field with standard Home Assistant metadata. Args: description: Field description unit: Signal unit (default: dBm) default: Default value or Pydantic default **kwargs: Additional Pydantic Field arguments Returns: Pydantic Field with signal strength metadata """ return _metadata_field( description, {"unit_of_measurement": unit, "device_class": "signal_strength"}, default, kwargs, )
[docs] def energy_field( description: str, *, unit: str = "kWh", default: Any = None, **kwargs: Any, ) -> Any: """Create an energy field with standard Home Assistant metadata. Args: description: Field description unit: Energy unit (default: kWh) default: Default value or Pydantic default **kwargs: Additional Pydantic Field arguments Returns: Pydantic Field with energy metadata """ return _metadata_field( description, {"unit_of_measurement": unit, "device_class": "energy"}, default, kwargs, )
[docs] def power_field( description: str, *, unit: str = "W", default: Any = None, **kwargs: Any, ) -> Any: """Create a power field with standard Home Assistant metadata. Args: description: Field description unit: Power unit (default: W) default: Default value or Pydantic default **kwargs: Additional Pydantic Field arguments Returns: Pydantic Field with power metadata """ return _metadata_field( description, {"unit_of_measurement": unit, "device_class": "power"}, default, kwargs, )