Source code for gecko_iot_client.models.temperature_control_zone

from enum import Enum
from typing import Any, Dict, Optional

from .abstract_zone import AbstractZone, ZoneType


[docs] class TemperatureControlZoneStatus(Enum): """Enum for temperature control zone status""" IDLE = 0 HEATING = 1 COOLING = 2 INVALID = 3 HEAT_PUMP_HEATING = 4 HEAT_PUMP_AND_HEATER_HEATING = 5 HEAT_PUMP_COOLING = 6 HEAT_PUMP_DEFROSTING = 7 HEAT_PUMP_ERROR = 8 @property def is_heating(self) -> bool: return self in { TemperatureControlZoneStatus.HEATING, TemperatureControlZoneStatus.HEAT_PUMP_HEATING, TemperatureControlZoneStatus.HEAT_PUMP_AND_HEATER_HEATING, }
[docs] class TemperatureControlMode: """Temperature control mode configuration"""
[docs] def __init__(self, eco: bool = False): """Initialize temperature control mode.""" self.eco = eco
[docs] @AbstractZone.register_zone_type(ZoneType.TEMPERATURE_CONTROL_ZONE) class TemperatureControlZone(AbstractZone): """State representation for temperature control zone v1 with validation"""
[docs] def __init__(self, zone_id: str, config: Dict[str, Any]): """Initialize TemperatureControlZone with zone_id and config.""" # Set default name if not provided if "name" not in config or config["name"] is None: config["name"] = f"Water Temperature {zone_id}" # Extract temperature control specific fields before parent init self.min_temperature_set_point_c = config.pop( "minTemperatureSetPointC", config.pop("min_temperature_set_point_c", None) ) self.max_temperature_set_point_c = config.pop( "maxTemperatureSetPointC", config.pop("max_temperature_set_point_c", None) ) # Initialize parent super().__init__( id=zone_id, zone_type=ZoneType.TEMPERATURE_CONTROL_ZONE, name=config.get("name"), config=config, ) # Initialize temperature control specific attributes self.status_: Optional[TemperatureControlZoneStatus] = ( self._convert_status_to_enum(getattr(self, "status_", None)) ) self.temperature_: Optional[float] = getattr(self, "temperature_", None) self.mode_: Optional[TemperatureControlMode] = getattr(self, "mode_", None) self.set_point: Optional[float] = getattr(self, "set_point", None) # Validate temperature ranges if values are present self._validate_temperature_range()
def _validate_temperature_range(self) -> None: """Validate temperature values are within acceptable range (-50 to 100 Celsius).""" if self.temperature_ is not None and not (-50.0 <= self.temperature_ <= 100.0): raise ValueError( f"Temperature {self.temperature_}°C is outside valid range (-50°C to 100°C)" ) if self.set_point is not None and not (-50.0 <= self.set_point <= 100.0): raise ValueError( f"Set point {self.set_point}°C is outside valid range (-50°C to 100°C)" ) if self.min_temperature_set_point_c is not None and not ( -50.0 <= self.min_temperature_set_point_c <= 100.0 ): raise ValueError( f"Min temperature {self.min_temperature_set_point_c}°C is outside valid range (-50°C to 100°C)" ) if self.max_temperature_set_point_c is not None and not ( -50.0 <= self.max_temperature_set_point_c <= 100.0 ): raise ValueError( f"Max temperature {self.max_temperature_set_point_c}°C is outside valid range (-50°C to 100°C)" ) @property def status(self) -> Optional[TemperatureControlZoneStatus]: return self.status_ @property def temperature(self) -> Optional[float]: return self.temperature_ @property def mode(self) -> Optional[TemperatureControlMode]: return self.mode_ @property def target_temperature(self) -> Optional[float]: return self.set_point @property def min_temperature_set_point_c_value(self) -> Optional[float]: return self.min_temperature_set_point_c @property def max_temperature_set_point_c_value(self) -> Optional[float]: return self.max_temperature_set_point_c
[docs] def __str__(self) -> str: """String representation of the TemperatureControlZone.""" temp_str = f"{self.temperature_}°C" if self.temperature_ is not None else "N/A" target_str = f"{self.set_point}°C" if self.set_point is not None else "N/A" status_str = self.status_.name if self.status_ else "N/A" eco_mode = self.mode_.eco if self.mode_ else False return ( f"TemperatureControlZone(name='{self.name}', id={self.id}, " f"temp={temp_str}, target={target_str}, status={status_str}, " f"eco_mode={eco_mode})" )
[docs] def set_target_temperature(self, temperature: float) -> None: """Set target temperature with validation against configured limits.""" if ( self.min_temperature_set_point_c is None or self.max_temperature_set_point_c is None ): raise ValueError( "Temperature limits not configured - cannot validate set point" ) if not ( self.min_temperature_set_point_c <= temperature <= self.max_temperature_set_point_c ): raise ValueError( f"Set point {temperature}°C is outside configured range " f"({self.min_temperature_set_point_c}°C to {self.max_temperature_set_point_c}°C)" ) self.set_point = temperature self._publish_desired_state({"setPoint": temperature})
[docs] def get_temperature_state(self) -> Dict[str, Any]: """Get the current temperature state as a simple dictionary.""" return { "current_temperature": self.temperature_, "target_temperature": self.set_point, "status": self.status_.name if self.status_ else None, "eco_mode": self.mode_.eco if self.mode_ else None, }
def _get_runtime_state_fields(self) -> set: return {"temperature_", "set_point", "mode_", "status_"} def _convert_status_to_enum( self, status_value: Any ) -> Optional[TemperatureControlZoneStatus]: """Convert status value to TemperatureControlZoneStatus enum.""" if status_value is None: return None try: # If it's already the correct enum type, return it if isinstance(status_value, TemperatureControlZoneStatus): return status_value # If it's an integer, try to convert by value if isinstance(status_value, int): return TemperatureControlZoneStatus(status_value) # If it's a string, try to convert by name if isinstance(status_value, str): # Try by name first (e.g., "HEATING") try: return TemperatureControlZoneStatus[status_value.upper()] except KeyError: # Try to convert string to int then to enum (e.g., "1" -> 1 -> HEATING) try: int_value = int(status_value) return TemperatureControlZoneStatus(int_value) except (ValueError, TypeError): pass except (ValueError, KeyError) as e: print( f"Warning: Could not convert status value {status_value} (type: {type(status_value)}) " f"to TemperatureControlZoneStatus: {e}" ) return None
[docs] def update_from_state(self, state: Dict[str, Any]) -> None: """Update temperature control zone from runtime state.""" if "temperature_" in state: self.temperature_ = state["temperature_"] if "setPoint" in state: self.set_point = state["setPoint"] # Handle status conversion status_value = state.get("status_") if status_value is not None: converted_status = self._convert_status_to_enum(status_value) if converted_status is not None: self.status_ = converted_status # Handle mode if "mode_" in state: mode_data = state["mode_"] if isinstance(mode_data, dict): self.mode_ = TemperatureControlMode(eco=mode_data.get("eco", False)) elif isinstance(mode_data, TemperatureControlMode): self.mode_ = mode_data