# Copyright 2021, 2022, 2024 Milan Meulemans. # # This file is part of aioaseko. # # aioaseko is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # aioaseko is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with aioaseko. If not, see . """aioAseko unit.""" from __future__ import annotations from dataclasses import dataclass from typing import TypeVar from .consumable import ElectrolyzerConsumable, LiquidConsumable from .status_value import StatusValues, StatusValueType, StringValue T = TypeVar("T", int, float, str, bool) @dataclass(frozen=True) class UnitNeverConnected: """Aseko Unit that has never connected.""" serial_number: str name: str | None note: str | None position: int online: bool @dataclass(frozen=True) class Unit: """Aseko Unit that has connected.""" serial_number: str name: str | None note: str | None online: bool has_warning: bool time_zone: str position: int brand_name: UnitBrandName | None consumables: list[LiquidConsumable | ElectrolyzerConsumable] status_values: StatusValues @property def air_temperature(self) -> float | None: """Return the air temperature.""" return self._converted_status_value(StatusValueType.AIR_TEMPERATURE, float) @property def cl_free(self) -> float | None: """Return the free chlorine.""" return self._converted_status_value(StatusValueType.CL_FREE, float) @property def dose(self) -> int | None: """Return the dose.""" return self._converted_status_value(StatusValueType.DOSE, int) @property def electrolyzer(self) -> int | None: """Return the electrolyzer.""" return self._converted_status_value(StatusValueType.ELECTROLYZER, int) @property def heating(self) -> bool | None: """Return the heating status.""" return self._converted_status_value(StatusValueType.HEATING, bool) @property def ph(self) -> float | None: """Return the pH value.""" return self._converted_status_value(StatusValueType.PH, float) @property def redox(self) -> int | None: """Return the redox value.""" return self._converted_status_value(StatusValueType.REDOX, int) @property def salinity(self) -> float | None: """Return the salinity.""" return self._converted_status_value(StatusValueType.SALINITY, float) @property def water_flow_to_probes(self) -> bool | None: """Return the water flow to probes.""" return self._converted_status_value(StatusValueType.WATER_FLOW_TO_PROBES, bool) @property def water_temperature(self) -> float | None: """Return the water temperature.""" return self._converted_status_value(StatusValueType.WATER_TEMPERATURE, float) def _status_value_string_value( self, status_value_type: StatusValueType ) -> str | None: """Return the status value of the given type, only for string values.""" for status_value in self.status_values.primary: if status_value.type == status_value_type: if isinstance(status_value.center, StringValue): return status_value.center.value raise ValueError(f"Value of {status_value_type} is not a string.") for status_value in self.status_values.secondary: if status_value.type == status_value_type: if isinstance(status_value.center, StringValue): return status_value.center.value raise ValueError(f"Value of {status_value_type} is not a string.") return None def _converted_status_value( self, status_value_type: StatusValueType, return_type: type[T] ) -> T | None: """Return the status value of the given status type, converted to the given type. Only for status value types with string value. """ value = self._status_value_string_value(status_value_type) if value is None or value == "---": return None if return_type == type[bool]: if value in ("YES", "ON"): return return_type(True) if value in ("NO", "OFF"): return return_type(False) raise ValueError(f"Value of {status_value_type} is not a boolean.") return return_type(value) @dataclass(frozen=True) class UnitBrandName: """Brand name of the unit.""" primary: str secondary: str